Skip to content

Commit

Permalink
chore: add revocation status selector to create credential form
Browse files Browse the repository at this point in the history
  • Loading branch information
Oleksandr Raspopov committed Jan 8, 2025
1 parent c6cb193 commit f42e8db
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 2 deletions.
2 changes: 2 additions & 0 deletions ui/src/adapters/api/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ export async function getAuthCredentialsByIDs({

export type CreateCredential = {
credentialSchema: string;
credentialStatusType: CredentialStatusType | null;
credentialSubject: Json;
displayMethod: CredentialDisplayMethod | null;
expiration: number | null;
Expand Down Expand Up @@ -556,6 +557,7 @@ export async function deleteLink({

export type CreateLink = {
credentialExpiration: string | null;
credentialStatusType: CredentialStatusType | null;
credentialSubject: Json;
displayMethod: CredentialDisplayMethod | null;
expiration: string | null;
Expand Down
18 changes: 16 additions & 2 deletions ui/src/adapters/parsers/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type CredentialIssuance = {
credentialDisplayMethod: { type: DisplayMethodType; url: string } | undefined;
credentialExpiration: Date | undefined;
credentialRefreshService: string | undefined;
credentialStatusType: CredentialStatusType | undefined;
credentialSubject: Record<string, unknown> | undefined;
mtProof: boolean;
signatureProof: boolean;
Expand Down Expand Up @@ -197,6 +198,7 @@ export const issuanceMethodFormDataParser = getStrictParser<IssuanceMethodFormDa

export type IssueCredentialFormData = {
credentialExpiration?: dayjs.Dayjs | null;
credentialStatusType: CredentialStatusType | null;
credentialSubject?: Record<string, unknown>;
displayMethod: { enabled: boolean; type: DisplayMethodType | ""; url: string };
proofTypes: ProofType[];
Expand All @@ -207,6 +209,7 @@ export type IssueCredentialFormData = {
const issueCredentialFormDataParser = getStrictParser<IssueCredentialFormData>()(
z.object({
credentialExpiration: dayjsInstanceParser.nullable().optional(),
credentialStatusType: z.nativeEnum(CredentialStatusType).nullable(),
credentialSubject: z.record(z.unknown()).optional(),
displayMethod: z.object({
enabled: z.boolean(),
Expand Down Expand Up @@ -237,8 +240,14 @@ export const credentialFormParser = getStrictParser<
issueCredential: issueCredentialFormDataParser,
})
.transform(({ issuanceMethod, issueCredential }, context) => {
const { credentialExpiration, credentialSubject, displayMethod, proofTypes, refreshService } =
issueCredential;
const {
credentialExpiration,
credentialStatusType,
credentialSubject,
displayMethod,
proofTypes,
refreshService,
} = issueCredential;
const { type } = issuanceMethod;

const baseIssuance = {
Expand All @@ -248,6 +257,7 @@ export const credentialFormParser = getStrictParser<
: undefined,
credentialExpiration: credentialExpiration ? credentialExpiration.toDate() : undefined,
credentialRefreshService: refreshService.enabled ? refreshService.url : undefined,
credentialStatusType: credentialStatusType || undefined,
credentialSubject,
mtProof: proofTypes.includes(ProofType.Iden3SparseMerkleTreeProof),
signatureProof: proofTypes.includes(ProofType.BJJSignature2021),
Expand Down Expand Up @@ -400,6 +410,7 @@ export function serializeCredentialLinkIssuance({
credentialDisplayMethod,
credentialExpiration,
credentialRefreshService,
credentialStatusType,
credentialSubject,
linkAccessibleUntil,
linkMaximumIssuance,
Expand All @@ -422,6 +433,7 @@ export function serializeCredentialLinkIssuance({
credentialExpiration: credentialExpiration
? serializeDate(credentialExpiration, "date-time")
: null,
credentialStatusType: credentialStatusType ?? null,
credentialSubject: serializedSchemaForm.data === undefined ? {} : serializedSchemaForm.data,
displayMethod: credentialDisplayMethod
? {
Expand Down Expand Up @@ -455,6 +467,7 @@ export function serializeCredentialIssuance({
credentialDisplayMethod,
credentialExpiration,
credentialRefreshService,
credentialStatusType,
credentialSubject,
did,
mtProof,
Expand All @@ -478,6 +491,7 @@ export function serializeCredentialIssuance({
return {
data: {
credentialSchema,
credentialStatusType: credentialStatusType ?? null,
credentialSubject: serializedSchemaForm.data === undefined ? {} : serializedSchemaForm.data,
displayMethod: credentialDisplayMethod
? {
Expand Down
1 change: 1 addition & 0 deletions ui/src/components/credentials/IssueCredential.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const defaultCredentialFormInput: CredentialFormInput = {
type: "directIssue",
},
issueCredential: {
credentialStatusType: null,
displayMethod: { enabled: false, type: "", url: "" },
proofTypes: [ProofType.BJJSignature2021],
refreshService: { enabled: false, url: "" },
Expand Down
70 changes: 70 additions & 0 deletions ui/src/components/credentials/IssueCredentialForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { generatePath } from "react-router-dom";
import { z } from "zod";

import { getDisplayMethods } from "src/adapters/api/display-method";
import { getSupportedBlockchains } from "src/adapters/api/identities";
import { getApiSchemas } from "src/adapters/api/schemas";
import { getJsonSchemaFromUrl } from "src/adapters/jsonSchemas";
import { buildAppError, jsonSchemaErrorToString, notifyError } from "src/adapters/parsers";
Expand All @@ -45,6 +46,7 @@ import {
ApiSchema,
AppError,
Attribute,
CredentialStatusType,
DisplayMethod,
JsonSchema,
ObjectAttribute,
Expand Down Expand Up @@ -124,6 +126,12 @@ export function IssueCredentialForm({
status: "pending",
});

const [credentialStatusTypes, setCredentialStatusTypes] = useState<
AsyncTask<CredentialStatusType[], AppError>
>({
status: "pending",
});

const [inputErrors, setInputErrors] = useState<InputErrors>();

const [refreshServiceChecked, setRefreshServiceChecked] = useState(
Expand Down Expand Up @@ -405,6 +413,40 @@ export function IssueCredentialForm({
[env, identifier]
);

const fetchBlockChains = useCallback(
async (signal: AbortSignal) => {
setCredentialStatusTypes((previousState) =>
isAsyncTaskDataAvailable(previousState)
? { data: previousState.data, status: "reloading" }
: { status: "loading" }
);

const response = await getSupportedBlockchains({
env,
signal,
});

if (response.success) {
const [, , blockchain = "", network = ""] = identifier.split(":");
const identityBlockchainNetworks =
response.data.successful.find(({ name }) => name === blockchain)?.networks || [];
const identityNetworkCredentialStatusTypes =
identityBlockchainNetworks.find(({ name }) => name === network)?.credentialStatus || [];

setCredentialStatusTypes({
data: identityNetworkCredentialStatusTypes,
status: "successful",
});
} else {
if (!isAbortedError(response.error)) {
setCredentialStatusTypes({ error: response.error, status: "failed" });
void message.error(response.error.message);
}
}
},
[env, message, identifier]
);

useEffect(() => {
const { aborter } = makeRequestAbortable(fetchSchemas);

Expand All @@ -417,6 +459,12 @@ export function IssueCredentialForm({
return aborter;
}, [fetchDisplayMethods]);

useEffect(() => {
const { aborter } = makeRequestAbortable(fetchBlockChains);

return aborter;
}, [fetchBlockChains]);

return (
<Form
form={form}
Expand Down Expand Up @@ -578,6 +626,28 @@ export function IssueCredentialForm({
</Form.Item>
</Space>
<Divider />

<Form.Item
label="Revocation status"
name="credentialStatusType"
style={{ marginBottom: 0 }}
>
<Select
className="full-width"
loading={isAsyncTaskStarting(credentialStatusTypes)}
placeholder="Valid URL of the display method"
>
{isAsyncTaskDataAvailable(credentialStatusTypes) &&
credentialStatusTypes.data.map((status) => (
<Select.Option key={status} value={status}>
{status}
</Select.Option>
))}
</Select>
</Form.Item>

<Divider />

<Form.Item style={{ marginBottom: 0 }}>
<Space direction="vertical">
<Form.Item
Expand Down

0 comments on commit f42e8db

Please sign in to comment.