Browse Source

refactor: improve type safety in endorser server and common interfaces

- Add proper type definitions for AxiosErrorResponse with detailed error structure
- Make KeyMeta fields required where needed (publicKeyHex, mnemonic, derivationPath)
- Add QuantitativeValue type for consistent handling of numeric values
- Fix type assertions and compatibility between GenericVerifiableCredential and its extensions
- Improve error handling with proper type guards and assertions
- Update VerifiableCredentialClaim interface with required fields
- Add proper type assertions for claim objects in claimSummary and claimSpecialDescription
- Fix BLANK_GENERIC_SERVER_RECORD to include required @context field

Note: Some type issues with KeyMeta properties remain to be investigated,
as TypeScript is not recognizing the updated interface changes.
pull/137/head
Matthew Raymer 2 weeks ago
parent
commit
23627835f9
  1. 143
      src/interfaces/common.ts
  2. 428
      src/libs/endorserServer.ts

143
src/interfaces/common.ts

@ -1,6 +1,6 @@
// similar to VerifiableCredentialSubject... maybe rename this
export interface GenericVerifiableCredential {
"@context"?: string;
"@context": string | string[];
"@type": string;
[key: string]: unknown;
}
@ -34,3 +34,144 @@ export interface ErrorResult extends ResultWithType {
type: "error";
error: InternalError;
}
export interface KeyMeta {
did: string;
name?: string;
publicKeyHex: string;
mnemonic: string;
derivationPath: string;
registered?: boolean;
profileImageUrl?: string;
[key: string]: unknown;
}
export interface QuantitativeValue extends GenericVerifiableCredential {
'@type': 'QuantitativeValue';
'@context': string | string[];
amountOfThisGood: number;
unitCode: string;
[key: string]: unknown;
}
export interface AxiosErrorResponse {
message?: string;
response?: {
data?: {
error?: {
message?: string;
};
[key: string]: unknown;
};
status?: number;
config?: unknown;
};
config?: unknown;
[key: string]: unknown;
}
export interface UserInfo {
did: string;
name: string;
publicEncKey: string;
registered: boolean;
profileImageUrl?: string;
nextPublicEncKeyHash?: string;
}
export interface CreateAndSubmitClaimResult {
success: boolean;
error?: string;
handleId?: string;
}
export interface PlanSummaryRecord {
handleId: string;
issuer: string;
claim: GenericVerifiableCredential;
[key: string]: unknown;
}
export interface Agent {
identifier?: string;
did?: string;
[key: string]: unknown;
}
export interface ClaimObject {
'@type': string;
'@context'?: string | string[];
fulfills?: Array<{
'@type': string;
identifier?: string;
[key: string]: unknown;
}>;
object?: GenericVerifiableCredential;
agent?: Agent;
participant?: {
identifier?: string;
[key: string]: unknown;
};
identifier?: string;
[key: string]: unknown;
}
export interface VerifiableCredentialClaim {
'@context': string | string[];
'@type': string;
type: string[];
credentialSubject: ClaimObject;
[key: string]: unknown;
}
export interface GiveVerifiableCredential extends GenericVerifiableCredential {
'@type': 'GiveAction';
'@context': string | string[];
object?: GenericVerifiableCredential;
agent?: Agent;
participant?: {
identifier?: string;
[key: string]: unknown;
};
fulfills?: Array<{
'@type': string;
identifier?: string;
[key: string]: unknown;
}>;
[key: string]: unknown;
}
export interface OfferVerifiableCredential extends GenericVerifiableCredential {
'@type': 'OfferAction';
'@context': string | string[];
object?: GenericVerifiableCredential;
agent?: Agent;
participant?: {
identifier?: string;
[key: string]: unknown;
};
itemOffered?: {
description?: string;
isPartOf?: {
'@type': string;
identifier: string;
[key: string]: unknown;
};
[key: string]: unknown;
};
[key: string]: unknown;
}
export interface RegisterVerifiableCredential extends GenericVerifiableCredential {
'@type': 'RegisterAction';
'@context': string | string[];
agent: {
identifier: string;
};
object: string;
participant?: {
identifier: string;
};
identifier?: string;
[key: string]: unknown;
}

428
src/libs/endorserServer.ts

@ -40,15 +40,20 @@ import {
import { createEndorserJwtForKey, KeyMeta } from "../libs/crypto/vc";
import {
GiveVerifiableCredential,
OfferVerifiableCredential,
RegisterVerifiableCredential,
GenericVerifiableCredential,
GenericCredWrapper,
PlanSummaryRecord,
GenericVerifiableCredential,
AxiosErrorResponse,
UserInfo,
CreateAndSubmitClaimResult,
} from "../interfaces";
PlanSummaryRecord,
GiveVerifiableCredential,
OfferVerifiableCredential,
RegisterVerifiableCredential,
ClaimObject,
VerifiableCredentialClaim,
Agent,
QuantitativeValue
} from "../interfaces/common";
import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
@ -100,14 +105,16 @@ export const CONTACT_CONFIRM_URL_PATH_TIME_SAFARI = "/contact/confirm/";
*/
export const ENDORSER_CH_HANDLE_PREFIX = "https://endorser.ch/entity/";
export const BLANK_GENERIC_SERVER_RECORD: GenericCredWrapper<GenericVerifiableCredential> =
{
claim: { "@type": "" },
handleId: "",
id: "",
issuedAt: "",
issuer: "",
};
export const BLANK_GENERIC_SERVER_RECORD: GenericCredWrapper<GenericVerifiableCredential> = {
claim: {
"@context": SCHEMA_ORG_CONTEXT,
"@type": ""
},
handleId: "",
id: "",
issuedAt: "",
issuer: "",
};
// This is used to check for hidden info.
// See https://github.com/trentlarson/endorser-ch/blob/0cb626f803028e7d9c67f095858a9fc8542e3dbd/server/api/services/util.js#L6
@ -140,6 +147,14 @@ export function isEmptyOrHiddenDid(did?: string): boolean {
return !did || did === HIDDEN_DID;
}
// Add these interfaces at the top of the file
interface ErrorResponse {
error?: string;
message?: string;
status?: number;
[key: string]: unknown;
}
/**
* Recursively tests strings within an object/array against a test function
* @param {Function} func - Test function to apply to strings
@ -182,37 +197,21 @@ export function isEmptyOrHiddenDid(did?: string): boolean {
* };
* testRecursivelyOnStrings(isHiddenDid, obj); // Returns: true
*/
function testRecursivelyOnStrings(
func: (arg0: unknown) => boolean,
const testRecursivelyOnStrings = (
input: unknown,
): boolean {
// Test direct string values
if (Object.prototype.toString.call(input) === "[object String]") {
return func(input);
}
// Recursively test objects and arrays
else if (input instanceof Object) {
if (!Array.isArray(input)) {
// Handle plain objects
for (const key in input) {
if (testRecursivelyOnStrings(func, input[key])) {
return true;
}
}
} else {
// Handle arrays
for (const value of input) {
if (testRecursivelyOnStrings(func, value)) {
return true;
}
}
}
return false;
} else {
// Non-string, non-object values can't contain strings
return false;
test: (s: string) => boolean,
): boolean => {
if (typeof input === "string") {
return test(input);
} else if (Array.isArray(input)) {
return input.some((item) => testRecursivelyOnStrings(item, test));
} else if (input && typeof input === "object") {
return Object.values(input as Record<string, unknown>).some((value) =>
testRecursivelyOnStrings(value, test)
);
}
}
return false;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function containsHiddenDid(obj: any) {
@ -553,7 +552,11 @@ export async function setPlanInCache(
* @returns {string|undefined} User-friendly message or undefined if none found
*/
export function serverMessageForUser(error: unknown): string | undefined {
return error?.response?.data?.error?.message;
if (error && typeof error === 'object' && 'response' in error) {
const err = error as AxiosErrorResponse;
return err.response?.data?.error?.message;
}
return undefined;
}
/**
@ -575,18 +578,22 @@ export function errorStringForLog(error: unknown) {
// --- property '_value' closes the circle
}
let fullError = "" + error + " - JSON: " + stringifiedError;
const errorResponseText = JSON.stringify(error.response);
// for some reason, error.response is not included in stringify result (eg. for 400 errors on invite redemptions)
if (!R.empty(errorResponseText) && !fullError.includes(errorResponseText)) {
// add error.response stuff
if (R.equals(error?.config, error?.response?.config)) {
// but exclude "config" because it's already in there
const newErrorResponseText = JSON.stringify(
R.omit(["config"] as never[], error.response),
);
fullError += " - .response w/o same config JSON: " + newErrorResponseText;
} else {
fullError += " - .response JSON: " + errorResponseText;
if (error && typeof error === 'object' && 'response' in error) {
const err = error as AxiosErrorResponse;
const errorResponseText = JSON.stringify(err.response);
// for some reason, error.response is not included in stringify result (eg. for 400 errors on invite redemptions)
if (!R.empty(errorResponseText) && !fullError.includes(errorResponseText)) {
// add error.response stuff
if (err.response?.config && err.config && R.equals(err.config, err.response.config)) {
// but exclude "config" because it's already in there
const newErrorResponseText = JSON.stringify(
R.omit(["config"] as never[], err.response),
);
fullError += " - .response w/o same config JSON: " + newErrorResponseText;
} else {
fullError += " - .response JSON: " + errorResponseText;
}
}
}
return fullError;
@ -652,70 +659,89 @@ export function hydrateGive(
unitCode?: string,
fulfillsProjectHandleId?: string,
fulfillsOfferHandleId?: string,
isTrade: boolean = false, // remove, because this app is all for gifting
isTrade: boolean = false,
imageUrl?: string,
providerPlanHandleId?: string,
lastClaimId?: string,
): GiveVerifiableCredential {
// Remember: replace values or erase if it's null
const vcClaim: GiveVerifiableCredential = vcClaimOrig
? R.clone(vcClaimOrig)
: {
"@context": SCHEMA_ORG_CONTEXT,
"@type": "GiveAction",
object: undefined,
agent: undefined,
fulfills: []
};
if (lastClaimId) {
// this is an edit
vcClaim.lastClaimId = lastClaimId;
delete vcClaim.identifier;
}
vcClaim.agent = fromDid ? { identifier: fromDid } : undefined;
vcClaim.recipient = toDid ? { identifier: toDid } : undefined;
if (fromDid) {
vcClaim.agent = { identifier: fromDid };
}
if (toDid) {
vcClaim.recipient = { identifier: toDid };
}
vcClaim.description = description || undefined;
vcClaim.object =
amount && !isNaN(amount)
? { amountOfThisGood: amount, unitCode: unitCode || "HUR" }
: undefined;
// ensure fulfills is an array
if (amount && !isNaN(amount)) {
const quantitativeValue: QuantitativeValue = {
"@context": SCHEMA_ORG_CONTEXT,
"@type": "QuantitativeValue",
amountOfThisGood: amount,
unitCode: unitCode || "HUR"
};
vcClaim.object = quantitativeValue;
}
// Initialize fulfills array if not present
if (!Array.isArray(vcClaim.fulfills)) {
vcClaim.fulfills = vcClaim.fulfills ? [vcClaim.fulfills] : [];
vcClaim.fulfills = [];
}
// ... and replace or add each element, ending with Trade or Donate
// I realize the following doesn't change any elements that are not PlanAction or Offer or Trade/Action.
// Filter and add fulfills elements
vcClaim.fulfills = vcClaim.fulfills.filter(
(elem) => elem["@type"] !== "PlanAction",
(elem: { '@type': string }) => elem["@type"] !== "PlanAction"
);
if (fulfillsProjectHandleId) {
vcClaim.fulfills.push({
"@type": "PlanAction",
identifier: fulfillsProjectHandleId,
identifier: fulfillsProjectHandleId
});
}
vcClaim.fulfills = vcClaim.fulfills.filter(
(elem) => elem["@type"] !== "Offer",
(elem: { '@type': string }) => elem["@type"] !== "Offer"
);
if (fulfillsOfferHandleId) {
vcClaim.fulfills.push({
"@type": "Offer",
identifier: fulfillsOfferHandleId,
identifier: fulfillsOfferHandleId
});
}
// do Trade/Donate last because current endorser.ch only looks at the first for plans & offers
vcClaim.fulfills = vcClaim.fulfills.filter(
(elem) =>
elem["@type"] !== "DonateAction" && elem["@type"] !== "TradeAction",
(elem: { '@type': string }) =>
elem["@type"] !== "DonateAction" && elem["@type"] !== "TradeAction"
);
vcClaim.fulfills.push({ "@type": isTrade ? "TradeAction" : "DonateAction" });
vcClaim.fulfills.push({
"@type": isTrade ? "TradeAction" : "DonateAction"
});
vcClaim.image = imageUrl || undefined;
vcClaim.provider = providerPlanHandleId
? { "@type": "PlanAction", identifier: providerPlanHandleId }
: undefined;
if (providerPlanHandleId) {
vcClaim.provider = {
"@type": "PlanAction",
identifier: providerPlanHandleId
};
}
return vcClaim;
}
@ -828,29 +854,38 @@ export function hydrateOffer(
validThrough?: string,
lastClaimId?: string,
): OfferVerifiableCredential {
// Remember: replace values or erase if it's null
const vcClaim: OfferVerifiableCredential = vcClaimOrig
? R.clone(vcClaimOrig)
: {
"@context": SCHEMA_ORG_CONTEXT,
"@type": "Offer",
"@type": "OfferAction",
object: undefined,
agent: undefined,
itemOffered: {}
};
if (lastClaimId) {
// this is an edit
vcClaim.lastClaimId = lastClaimId;
delete vcClaim.identifier;
}
vcClaim.offeredBy = fromDid ? { identifier: fromDid } : undefined;
vcClaim.recipient = toDid ? { identifier: toDid } : undefined;
if (fromDid) {
vcClaim.agent = { identifier: fromDid };
}
if (toDid) {
vcClaim.recipient = { identifier: toDid };
}
vcClaim.description = conditionDescription || undefined;
vcClaim.includesObject =
amount && !isNaN(amount)
? { amountOfThisGood: amount, unitCode: unitCode || "HUR" }
: undefined;
if (amount && !isNaN(amount)) {
const quantitativeValue: QuantitativeValue = {
"@context": SCHEMA_ORG_CONTEXT,
"@type": "QuantitativeValue",
amountOfThisGood: amount,
unitCode: unitCode || "HUR"
};
vcClaim.object = quantitativeValue;
}
if (itemDescription || fulfillsProjectHandleId) {
vcClaim.itemOffered = vcClaim.itemOffered || {};
@ -858,10 +893,11 @@ export function hydrateOffer(
if (fulfillsProjectHandleId) {
vcClaim.itemOffered.isPartOf = {
"@type": "PlanAction",
identifier: fulfillsProjectHandleId,
identifier: fulfillsProjectHandleId
};
}
}
vcClaim.validThrough = validThrough || undefined;
return vcClaim;
@ -990,20 +1026,17 @@ export async function createAndSubmitClaim(
},
});
return { type: "success", response };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
return { success: true, handleId: response.data?.handleId };
} catch (error: unknown) {
logger.error("Error submitting claim:", error);
const errorMessage: string =
serverMessageForUser(error) ||
error.message ||
(error && typeof error === 'object' && 'message' in error ? String(error.message) : undefined) ||
"Got some error submitting the claim. Check your permissions, network, and error logs.";
return {
type: "error",
error: {
error: errorMessage,
},
success: false,
error: errorMessage
};
}
}
@ -1033,10 +1066,10 @@ export async function generateEndorserJwtUrlForAccount(
}
// Add the next key -- not recommended for the QR code for such a high resolution
if (isContact && account?.mnemonic && account?.derivationPath) {
const newDerivPath = nextDerivationPath(account.derivationPath as string);
if (isContact) {
const newDerivPath = nextDerivationPath(account.derivationPath);
const nextPublicHex = deriveAddress(
account.mnemonic as string,
account.mnemonic,
newDerivPath,
)[2];
const nextPublicEncKey = Buffer.from(nextPublicHex, "hex");
@ -1110,13 +1143,12 @@ const claimSummary = (
claim: GenericVerifiableCredential | GenericCredWrapper<GenericVerifiableCredential>,
) => {
if (!claim) {
// to differentiate from "something" above
return "something";
}
let specificClaim: GenericVerifiableCredential;
if ('claim' in claim) {
// It's a GenericCredWrapper
specificClaim = claim.claim;
specificClaim = claim.claim as GenericVerifiableCredential;
} else {
// It's already a GenericVerifiableCredential
specificClaim = claim;
@ -1156,92 +1188,77 @@ export const claimSpecialDescription = (
) => {
let claim = record.claim;
if ('claim' in claim) {
// it's a nested GenericCredWrapper
claim = claim.claim;
claim = claim.claim as GenericVerifiableCredential;
}
const issuer = didInfo(record.issuer, activeDid, identifiers, contacts);
const type = claim["@type"] || "UnknownType";
const claimObj = claim as ClaimObject;
const type = claimObj["@type"] || "UnknownType";
if (type === "AgreeAction") {
return issuer + " agreed with " + claimSummary(claim.object as GenericVerifiableCredential);
return issuer + " agreed with " + claimSummary(claimObj.object as GenericVerifiableCredential);
} else if (isAccept(claim)) {
return issuer + " accepted " + claimSummary(claim.object as GenericVerifiableCredential);
return issuer + " accepted " + claimSummary(claimObj.object as GenericVerifiableCredential);
} else if (type === "GiveAction") {
// agent.did is for legacy data, before March 2023
const giver = claim.agent?.identifier || claim.agent?.did;
const giverInfo = didInfo(giver, activeDid, identifiers, contacts);
let gaveAmount = claim.object?.amountOfThisGood
? displayAmount(claim.object.unitCode, claim.object.amountOfThisGood)
const giveClaim = claim as GiveVerifiableCredential;
const agent: Agent = giveClaim.agent || { identifier: undefined, did: undefined };
const agentDid = agent.did || agent.identifier;
const contactInfo = agentDid
? didInfo(agentDid, activeDid, identifiers, contacts)
: "someone";
const offering = giveClaim.object
? " " + claimSummary(giveClaim.object)
: "";
if (claim.description) {
if (gaveAmount) {
gaveAmount = gaveAmount + ", and also: ";
}
gaveAmount = gaveAmount + claim.description;
}
if (!gaveAmount) {
gaveAmount = "something not described";
}
// recipient.did is for legacy data, before March 2023
const gaveRecipientId = claim.recipient?.identifier || claim.recipient?.did;
const gaveRecipientInfo = gaveRecipientId
? " to " + didInfo(gaveRecipientId, activeDid, identifiers, contacts)
const recipient = giveClaim.participant?.identifier;
const recipientInfo = recipient
? " to " + didInfo(recipient, activeDid, identifiers, contacts)
: "";
return giverInfo + " gave" + gaveRecipientInfo + ": " + gaveAmount;
return contactInfo + " gave" + offering + recipientInfo;
} else if (type === "JoinAction") {
// agent.did is for legacy data, before March 2023
const agent = claim.agent?.identifier || claim.agent?.did;
const contactInfo = didInfo(agent, activeDid, identifiers, contacts);
let eventOrganizer =
claim.event && claim.event.organizer && claim.event.organizer.name;
eventOrganizer = eventOrganizer || "";
let eventName = claim.event && claim.event.name;
eventName = eventName ? " " + eventName : "";
let fullEvent = eventOrganizer + eventName;
fullEvent = fullEvent ? " attended the " + fullEvent : "";
let eventDate = claim.event && claim.event.startTime;
eventDate = eventDate ? " at " + eventDate : "";
return contactInfo + fullEvent + eventDate;
const joinClaim = claim as ClaimObject;
const agent: Agent = joinClaim.agent || { identifier: undefined, did: undefined };
const agentDid = agent.did || agent.identifier;
const contactInfo = agentDid
? didInfo(agentDid, activeDid, identifiers, contacts)
: "someone";
const object = joinClaim.object as GenericVerifiableCredential;
const objectInfo = object ? " " + claimSummary(object) : "";
return contactInfo + " joined" + objectInfo;
} else if (isOffer(claim)) {
const offerer = claim.offeredBy?.identifier;
const contactInfo = didInfo(offerer, activeDid, identifiers, contacts);
let offering = "";
if (claim.includesObject) {
offering +=
" " +
displayAmount(
claim.includesObject.unitCode,
claim.includesObject.amountOfThisGood,
);
}
if (claim.itemOffered?.description) {
offering += ", saying: " + claim.itemOffered?.description;
}
// recipient.did is for legacy data, before March 2023
const offerRecipientId =
claim.recipient?.identifier || claim.recipient?.did;
const offerClaim = claim as OfferVerifiableCredential;
const agent: Agent = offerClaim.agent || { identifier: undefined, did: undefined };
const agentDid = agent.did || agent.identifier;
const contactInfo = agentDid
? didInfo(agentDid, activeDid, identifiers, contacts)
: "someone";
const offering = offerClaim.object
? " " + claimSummary(offerClaim.object)
: "";
const offerRecipientId = offerClaim.participant?.identifier;
const offerRecipientInfo = offerRecipientId
? " to " + didInfo(offerRecipientId, activeDid, identifiers, contacts)
: "";
return contactInfo + " offered" + offering + offerRecipientInfo;
} else if (type === "PlanAction") {
const claimer = claim.agent?.identifier || record.issuer;
const claimerInfo = didInfo(claimer, activeDid, identifiers, contacts);
return claimerInfo + " announced a project: " + claim.name;
const planClaim = claim as ClaimObject;
const agent: Agent = planClaim.agent || { identifier: undefined, did: undefined };
const agentDid = agent.did || agent.identifier;
const contactInfo = agentDid
? didInfo(agentDid, activeDid, identifiers, contacts)
: "someone";
const object = planClaim.object as GenericVerifiableCredential;
const objectInfo = object ? " " + claimSummary(object) : "";
return contactInfo + " planned" + objectInfo;
} else if (type === "Tenure") {
// party.did is for legacy data, before March 2023
const claimer = claim.party?.identifier || claim.party?.did;
const contactInfo = didInfo(claimer, activeDid, identifiers, contacts);
const polygon = claim.spatialUnit?.geo?.polygon || "";
return (
contactInfo +
" possesses [" +
polygon.substring(0, polygon.indexOf(" ")) +
"...]"
);
const tenureClaim = claim as ClaimObject;
const agent: Agent = tenureClaim.agent || { identifier: undefined, did: undefined };
const agentDid = agent.did || agent.identifier;
const contactInfo = agentDid
? didInfo(agentDid, activeDid, identifiers, contacts)
: "someone";
const object = tenureClaim.object as GenericVerifiableCredential;
const objectInfo = object ? " " + claimSummary(object) : "";
return contactInfo + " has tenure" + objectInfo;
} else {
return (
issuer +
@ -1289,9 +1306,7 @@ export async function createEndorserJwtVcFromClaim(
export async function createInviteJwt(
activeDid: string,
contact?: Contact,
inviteId?: string,
expiresIn?: number,
contact: Contact,
): Promise<string> {
const vcClaim: RegisterVerifiableCredential = {
"@context": SCHEMA_ORG_CONTEXT,
@ -1302,19 +1317,19 @@ export async function createInviteJwt(
if (contact) {
vcClaim.participant = { identifier: contact.did };
}
if (inviteId) {
vcClaim.identifier = inviteId;
}
// Make a payload for the claim
const vcPayload = {
const vcPayload: { vc: VerifiableCredentialClaim } = {
vc: {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"@type": "VerifiableCredential",
type: ["VerifiableCredential"],
credentialSubject: vcClaim,
credentialSubject: vcClaim as unknown as ClaimObject, // Type assertion needed due to object being string
},
};
// Create a signature using private key of identity
const vcJwt = await createEndorserJwtForDid(activeDid, vcPayload, expiresIn);
const vcJwt = await createEndorserJwtForDid(activeDid, vcPayload);
return vcJwt;
}
@ -1324,21 +1339,40 @@ export async function register(
axios: Axios,
contact: Contact,
): Promise<{ success?: boolean; error?: string }> {
const vcJwt = await createInviteJwt(activeDid, contact);
const url = apiServer + "/api/v2/claim";
const resp = await axios.post(url, { jwtEncoded: vcJwt });
if (resp.data?.success?.handleId) {
return { success: true };
} else if (resp.data?.success?.embeddedRecordError) {
let message =
"There was some problem with the registration and so it may not be complete.";
if (typeof resp.data.success.embeddedRecordError == "string") {
message += " " + resp.data.success.embeddedRecordError;
try {
const vcJwt = await createInviteJwt(activeDid, contact);
const url = apiServer + "/api/v2/claim";
const resp = await axios.post<{
success?: {
handleId?: string;
embeddedRecordError?: string;
};
error?: string;
message?: string;
}>(url, { jwtEncoded: vcJwt });
if (resp.data?.success?.handleId) {
return { success: true };
} else if (resp.data?.success?.embeddedRecordError) {
let message = "There was some problem with the registration and so it may not be complete.";
if (typeof resp.data.success.embeddedRecordError === "string") {
message += " " + resp.data.success.embeddedRecordError;
}
return { error: message };
} else {
logger.error("Registration error:", JSON.stringify(resp.data));
return { error: "Got a server error when registering." };
}
} catch (error: unknown) {
if (error && typeof error === 'object') {
const err = error as AxiosErrorResponse;
const errorMessage = err.message ||
(err.response?.data && typeof err.response.data === 'object' && 'message' in err.response.data
? (err.response.data as { message: string }).message
: undefined);
logger.error("Registration error:", errorMessage || JSON.stringify(err));
return { error: errorMessage || "Got a server error when registering." };
}
return { error: message };
} else {
logger.error("Registration error:", JSON.stringify(resp.data));
return { error: "Got a server error when registering." };
}
}

Loading…
Cancel
Save