add ability to edit a GiveAction

This commit is contained in:
2024-07-23 20:14:07 -06:00
parent 6456ce8dcc
commit d724d8093c
11 changed files with 327 additions and 102 deletions

View File

@@ -67,7 +67,7 @@ export async function createEndorserJwtForKey(
* The SimpleSigner returns a configured function for signing data.
*
* @example
* const signer = SimpleSigner(import.meta.env.PRIVATE_KEY)
* const signer = SimpleSigner(privateKeyHexString)
* signer(data, (err, signature) => {
* ...
* })

View File

@@ -48,29 +48,31 @@ export interface ClaimResult {
}
export interface GenericVerifiableCredential {
"@context": string;
"@context"?: string;
"@type": string;
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
export interface GenericCredWrapper extends GenericVerifiableCredential {
export interface GenericCredWrapper<T extends GenericVerifiableCredential> {
"@context": string;
"@type": string;
handleId: string;
id: string;
issuedAt: string;
issuer: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
claim: Record<string, any>;
claim: T;
claimType?: string;
}
export const BLANK_GENERIC_SERVER_RECORD: GenericCredWrapper = {
"@context": SCHEMA_ORG_CONTEXT,
"@type": "",
claim: {},
handleId: "",
id: "",
issuedAt: "",
issuer: "",
};
export const BLANK_GENERIC_SERVER_RECORD: GenericCredWrapper<GenericVerifiableCredential> =
{
"@context": SCHEMA_ORG_CONTEXT,
"@type": "",
claim: { "@type": "" },
handleId: "",
id: "",
issuedAt: "",
issuer: "",
};
// a summary record; the VC is found the fullClaim field
export interface GiveSummaryRecord {
@@ -123,7 +125,7 @@ export interface PlanSummaryRecord {
// Note that previous VCs may have additional fields.
// https://endorser.ch/doc/html/transactions.html#id4
export interface GiveVerifiableCredential {
export interface GiveVerifiableCredential extends GenericVerifiableCredential {
"@context"?: string; // optional when embedded, eg. in an Agree
"@type": "GiveAction";
agent?: { identifier: string };
@@ -191,7 +193,7 @@ export interface PlanData {
*/
issuerDid: string;
/**
* The Identier of the project -- different from jwtId, needs to be fixed
* The identifier of the project -- different from jwtId, needs to be fixed
**/
rowid?: string;
}
@@ -562,8 +564,9 @@ export async function setPlanInCache(
/**
* Construct GiveAction VC for submission to server
*/
export function constructGive(
fromDid?: string | null,
export function hydrateGive(
vcClaimOrig?: GiveVerifiableCredential,
fromDid?: string,
toDid?: string,
description?: string,
amount?: number,
@@ -572,42 +575,68 @@ export function constructGive(
fulfillsOfferHandleId?: string,
isTrade: boolean = false,
imageUrl?: string,
lastClaimId?: string,
): GiveVerifiableCredential {
const vcClaim: GiveVerifiableCredential = {
"@context": SCHEMA_ORG_CONTEXT,
"@type": "GiveAction",
recipient: toDid ? { identifier: toDid } : undefined,
agent: fromDid ? { identifier: fromDid } : undefined,
description: description || undefined,
object: amount
? { amountOfThisGood: amount, unitCode: unitCode || "HUR" }
: undefined,
fulfills: [{ "@type": isTrade ? "TradeAction" : "DonateAction" }],
};
// Remember: replace values or erase if it's null
const vcClaim: GiveVerifiableCredential = vcClaimOrig
? R.clone(vcClaimOrig)
: {
"@context": SCHEMA_ORG_CONTEXT,
"@type": "GiveAction",
};
if (lastClaimId) {
vcClaim.lastClaimId = lastClaimId;
delete vcClaim.identifier;
}
vcClaim.agent = fromDid ? { identifier: fromDid } : undefined;
vcClaim.recipient = toDid ? { identifier: toDid } : undefined;
vcClaim.description = description || undefined;
vcClaim.object = amount
? { amountOfThisGood: amount, unitCode: unitCode || "HUR" }
: undefined;
// ensure fulfills is an array
if (!Array.isArray(vcClaim.fulfills)) {
vcClaim.fulfills = vcClaim.fulfills ? [vcClaim.fulfills] : [];
}
// ... and replace or add each element, ending with Trade or Donate
// I realize this doesn't change any elements that are not PlanAction or Offer or Trade/Action.
if (fulfillsProjectHandleId) {
vcClaim.fulfills = vcClaim.fulfills || []; // weird that it won't typecheck without this
vcClaim.fulfills = vcClaim.fulfills.filter(
(elem) => elem["@type"] !== "PlanAction",
);
vcClaim.fulfills.push({
"@type": "PlanAction",
identifier: fulfillsProjectHandleId,
});
}
if (fulfillsOfferHandleId) {
vcClaim.fulfills = vcClaim.fulfills || []; // weird that it won't typecheck without this
vcClaim.fulfills = vcClaim.fulfills.filter(
(elem) => elem["@type"] !== "Offer",
);
vcClaim.fulfills.push({
"@type": "Offer",
identifier: fulfillsOfferHandleId,
});
}
if (imageUrl) {
vcClaim.image = imageUrl;
}
// 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",
);
vcClaim.fulfills.push({ "@type": isTrade ? "TradeAction" : "DonateAction" });
vcClaim.image = imageUrl || undefined;
return vcClaim;
}
/**
* For result, see https://api.endorser.ch/api-docs/#/claims/post_api_v2_claim
* For result, see https://api.endorser.ch/api-docs/#/claims/post_api_v2_claim
*
* @param identity
* @param fromDid may be null
* @param toDid
* @param description may be null; should have this or amount
@@ -617,7 +646,7 @@ export async function createAndSubmitGive(
axios: Axios,
apiServer: string,
issuerDid: string,
fromDid?: string | null,
fromDid?: string,
toDid?: string,
description?: string,
amount?: number,
@@ -627,7 +656,8 @@ export async function createAndSubmitGive(
isTrade: boolean = false,
imageUrl?: string,
): Promise<CreateAndSubmitClaimResult> {
const vcClaim = constructGive(
const vcClaim = hydrateGive(
undefined,
fromDid,
toDid,
description,
@@ -639,7 +669,51 @@ export async function createAndSubmitGive(
imageUrl,
);
return createAndSubmitClaim(
vcClaim as GenericCredWrapper,
vcClaim as GenericVerifiableCredential,
issuerDid,
apiServer,
axios,
);
}
/**
* For result, see https://api.endorser.ch/api-docs/#/claims/post_api_v2_claim
*
* @param fromDid may be null
* @param toDid
* @param description may be null; should have this or amount
* @param amount may be null; should have this or description
*/
export async function editAndSubmitGive(
axios: Axios,
apiServer: string,
fullClaim: GenericCredWrapper<GiveVerifiableCredential>,
issuerDid: string,
fromDid?: string,
toDid?: string,
description?: string,
amount?: number,
unitCode?: string,
fulfillsProjectHandleId?: string,
fulfillsOfferHandleId?: string,
isTrade: boolean = false,
imageUrl?: string,
): Promise<CreateAndSubmitClaimResult> {
const vcClaim = hydrateGive(
fullClaim.claim,
fromDid,
toDid,
description,
amount,
unitCode,
fulfillsProjectHandleId,
fulfillsOfferHandleId,
isTrade,
imageUrl,
fullClaim.id,
);
return createAndSubmitClaim(
vcClaim as GenericVerifiableCredential,
issuerDid,
apiServer,
axios,
@@ -692,7 +766,7 @@ export async function createAndSubmitOffer(
};
}
return createAndSubmitClaim(
vcClaim as GenericCredWrapper,
vcClaim as OfferVerifiableCredential,
issuerDid,
apiServer,
axios,
@@ -751,7 +825,7 @@ export async function createAndSubmitClaim(
return { type: "success", response };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
console.error("Error creating claim:", error);
console.error("Error submitting claim:", error);
const errorMessage: string =
error.response?.data?.error?.message ||
error.message ||
@@ -820,24 +894,29 @@ export const capitalizeAndInsertSpacesBeforeCaps = (text: string) => {
similar code is also contained in endorser-mobile
**/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const claimSummary = (claim: Record<string, any>) => {
const claimSummary = (
claim: GenericCredWrapper<GenericVerifiableCredential>,
) => {
if (!claim) {
// to differentiate from "something" above
return "something";
}
let specificClaim:
| GenericVerifiableCredential
| GenericCredWrapper<GenericVerifiableCredential> = claim;
if (claim.claim) {
// probably a Verified Credential
// eslint-disable-next-line @typescript-eslint/no-explicit-any
claim = claim.claim as Record<string, any>;
specificClaim = claim.claim;
}
if (Array.isArray(claim)) {
if (claim.length === 1) {
claim = claim[0];
if (Array.isArray(specificClaim)) {
if (specificClaim.length === 1) {
specificClaim = specificClaim[0];
} else {
return "multiple claims";
}
}
const type = claim["@type"];
const type = specificClaim["@type"];
if (!type) {
return "a claim";
} else {
@@ -858,7 +937,7 @@ const claimSummary = (claim: Record<string, any>) => {
similar code is also contained in endorser-mobile
**/
export const claimSpecialDescription = (
record: GenericCredWrapper,
record: GenericCredWrapper<GenericVerifiableCredential>,
activeDid: string,
identifiers: Array<string>,
contacts: Array<Contact>,
@@ -952,7 +1031,11 @@ export const claimSpecialDescription = (
"...]"
);
} else {
return issuer + " declared " + claimSummary(claim as GenericCredWrapper);
return (
issuer +
" declared " +
claimSummary(claim as GenericCredWrapper<GenericVerifiableCredential>)
);
}
};

View File

@@ -11,7 +11,12 @@ import {
MASTER_SETTINGS_KEY,
} from "@/db/tables/settings";
import { deriveAddress, generateSeed, newIdentifier } from "@/libs/crypto";
import { GenericCredWrapper, containsHiddenDid } from "@/libs/endorserServer";
import {
containsHiddenDid,
GenericCredWrapper,
GenericVerifiableCredential,
OfferVerifiableCredential,
} from "@/libs/endorserServer";
import * as serverUtil from "@/libs/endorserServer";
import { registerCredential } from "@/libs/crypto/vc/passkeyDidPeer";
@@ -79,7 +84,9 @@ export const isGlobalUri = (uri: string) => {
return uri && uri.match(new RegExp(/^[A-Za-z][A-Za-z0-9+.-]+:/));
};
export const isGiveAction = (veriClaim: GenericCredWrapper) => {
export const isGiveAction = (
veriClaim: GenericCredWrapper<GenericVerifiableCredential>,
) => {
return veriClaim.claimType === "GiveAction";
};
@@ -95,7 +102,7 @@ export const doCopyTwoSecRedo = (text: string, fn: () => void) => {
* @param veriClaim is expected to have fields: claim, claimType, and issuer
*/
export const isGiveRecordTheUserCanConfirm = (
veriClaim: GenericCredWrapper,
veriClaim: GenericCredWrapper<GenericVerifiableCredential>,
activeDid: string,
confirmerIdList: string[] = [],
) => {
@@ -111,9 +118,9 @@ export const isGiveRecordTheUserCanConfirm = (
* @returns the DID of the person who offered, or undefined if hidden
* @param veriClaim is expected to have fields: claim and issuer
*/
export const offerGiverDid: (arg0: GenericCredWrapper) => string | undefined = (
veriClaim,
) => {
export const offerGiverDid: (
arg0: GenericCredWrapper<OfferVerifiableCredential>,
) => string | undefined = (veriClaim) => {
let giver;
if (
veriClaim.claim.offeredBy?.identifier &&
@@ -130,8 +137,13 @@ export const offerGiverDid: (arg0: GenericCredWrapper) => string | undefined = (
* @returns true if the user can fulfill the offer
* @param veriClaim is expected to have fields: claim, claimType, and issuer
*/
export const canFulfillOffer = (veriClaim: GenericCredWrapper) => {
return !!(veriClaim.claimType === "Offer" && offerGiverDid(veriClaim));
export const canFulfillOffer = (
veriClaim: GenericCredWrapper<GenericVerifiableCredential>,
) => {
return !!(
veriClaim.claimType === "Offer" &&
offerGiverDid(veriClaim as GenericCredWrapper<OfferVerifiableCredential>)
);
};
// return object with paths and arrays of DIDs for any keys ending in "VisibleToDid"