From 269d00a096cd8f70961b42ede4ff7a05e4a287c3 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Fri, 16 Aug 2024 15:58:54 -0600 Subject: [PATCH 1/3] start with offer-edit --- src/libs/endorserServer.ts | 168 +++++++--- src/views/GiftedDetails.vue | 1 - src/views/OfferDetails.vue | 624 ++++++++++++++++++++++++++++++++++++ 3 files changed, 748 insertions(+), 45 deletions(-) create mode 100644 src/views/OfferDetails.vue diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index d7475a8..619afa1 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -48,7 +48,7 @@ export interface ClaimResult { } export interface GenericVerifiableCredential { - "@context"?: string; + "@context"?: string; // optional when embedded, eg. in an Agree "@type": string; [key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any } @@ -139,13 +139,13 @@ export interface GiveVerifiableCredential extends GenericVerifiableCredential { // Note that previous VCs may have additional fields. // https://endorser.ch/doc/html/transactions.html#id8 -export interface OfferVerifiableCredential { - "@context"?: string; // optional when embedded, eg. in an Agree +export interface OfferVerifiableCredential extends GenericVerifiableCredential { + "@context"?: string; // optional when embedded... though it doesn't make sense to agree to an offer "@type": "Offer"; - description?: string; + description?: string; // conditions for the offer includesObject?: { amountOfThisGood: number; unitCode: string }; itemOffered?: { - description?: string; + description?: string; // description of the item isPartOf?: { identifier?: string; lastClaimId?: string; "@type"?: string }; }; offeredBy?: { identifier: string }; @@ -155,7 +155,7 @@ export interface OfferVerifiableCredential { // Note that previous VCs may have additional fields. // https://endorser.ch/doc/html/transactions.html#id7 -export interface PlanVerifiableCredential { +export interface PlanVerifiableCredential extends GenericVerifiableCredential { "@context": "https://schema.org"; "@type": "PlanAction"; name: string; @@ -563,6 +563,8 @@ export async function setPlanInCache( /** * Construct GiveAction VC for submission to server + * + * @param lastClaimId supplied when editing a previous claim */ export function hydrateGive( vcClaimOrig?: GiveVerifiableCredential, @@ -587,6 +589,7 @@ export function hydrateGive( }; if (lastClaimId) { + // this is an edit vcClaim.lastClaimId = lastClaimId; delete vcClaim.identifier; } @@ -594,16 +597,17 @@ export function hydrateGive( 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; + vcClaim.object = + amount && !isNaN(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. + // I realize the following doesn't change any elements that are not PlanAction or Offer or Trade/Action. vcClaim.fulfills = vcClaim.fulfills.filter( (elem) => elem["@type"] !== "PlanAction", ); @@ -639,8 +643,8 @@ export function hydrateGive( * * @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 + * @param description may be null + * @param amount may be null */ export async function createAndSubmitGive( axios: Axios, @@ -667,6 +671,7 @@ export async function createAndSubmitGive( fulfillsOfferHandleId, isTrade, imageUrl, + undefined, ); return createAndSubmitClaim( vcClaim as GenericVerifiableCredential, @@ -680,9 +685,9 @@ export async function createAndSubmitGive( * 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 + * @param toDid may be null if project is provided + * @param description may be null + * @param amount may be null */ export async function editAndSubmitGive( axios: Axios, @@ -720,13 +725,69 @@ export async function editAndSubmitGive( ); } +/** + * Construct Offer VC for submission to server + * + * @param lastClaimId supplied when editing a previous claim + */ +export function hydrateOffer( + vcClaimOrig?: OfferVerifiableCredential, + fromDid?: string, + toDid?: string, + conditionDescription?: string, + amount?: number, + unitCode?: string, + offeringDescription?: string, + fulfillsProjectHandleId?: string, + 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", + }; + + if (lastClaimId) { + // this is an edit + vcClaim.lastClaimId = lastClaimId; + delete vcClaim.identifier; + } + + vcClaim.offeredBy = fromDid ? { identifier: fromDid } : undefined; + vcClaim.recipient = toDid ? { identifier: toDid } : undefined; + vcClaim.description = conditionDescription || undefined; + + vcClaim.includesObject = + amount && !isNaN(amount) + ? { amountOfThisGood: amount, unitCode: unitCode || "HUR" } + : undefined; + + if (offeringDescription || fulfillsProjectHandleId) { + vcClaim.itemOffered = vcClaim.itemOffered || {}; + vcClaim.itemOffered.description = offeringDescription || undefined; + if (fulfillsProjectHandleId) { + vcClaim.itemOffered.isPartOf = { + "@type": "PlanAction", + identifier: fulfillsProjectHandleId, + }; + } + } + vcClaim.validThrough = validThrough || undefined; + + return vcClaim; +} + /** * For result, see https://api.endorser.ch/api-docs/#/claims/post_api_v2_claim * * @param identity - * @param description may be null; should have this or amount - * @param amount may be null; should have this or description - * @param expirationDate ISO 8601 date string YYYY-MM-DD (may be null) + * @param description may be null + * @param amount may be null + * @param validThrough ISO 8601 date string YYYY-MM-DD (may be null) * @param fulfillsProjectHandleId ID of project to which this contributes (may be null) */ export async function createAndSubmitOffer( @@ -736,35 +797,54 @@ export async function createAndSubmitOffer( description?: string, amount?: number, unitCode?: string, - expirationDate?: string, + validThrough?: string, recipientDid?: string, fulfillsProjectHandleId?: string, ): Promise { - const vcClaim: OfferVerifiableCredential = { - "@context": SCHEMA_ORG_CONTEXT, - "@type": "Offer", - offeredBy: { identifier: issuerDid }, - validThrough: expirationDate || undefined, - }; - if (amount) { - vcClaim.includesObject = { - amountOfThisGood: amount, - unitCode: unitCode || "HUR", - }; - } - if (description) { - vcClaim.itemOffered = { description }; - } - if (recipientDid) { - vcClaim.recipient = { identifier: recipientDid }; - } - if (fulfillsProjectHandleId) { - vcClaim.itemOffered = vcClaim.itemOffered || {}; - vcClaim.itemOffered.isPartOf = { - "@type": "PlanAction", - identifier: fulfillsProjectHandleId, - }; - } + const vcClaim = hydrateOffer( + undefined, + issuerDid, + recipientDid, + description, + amount, + unitCode, + description, + fulfillsProjectHandleId, + validThrough, + undefined, + ); + return createAndSubmitClaim( + vcClaim as OfferVerifiableCredential, + issuerDid, + apiServer, + axios, + ); +} + +export async function editAndSubmitOffer( + axios: Axios, + apiServer: string, + fullClaim: GenericCredWrapper, + issuerDid: string, + description?: string, + amount?: number, + unitCode?: string, + validThrough?: string, + recipientDid?: string, + fulfillsProjectHandleId?: string, +): Promise { + const vcClaim = hydrateOffer( + fullClaim.claim, + issuerDid, + recipientDid, + description, + amount, + unitCode, + description, + fulfillsProjectHandleId, + validThrough, + fullClaim.id, + ); return createAndSubmitClaim( vcClaim as OfferVerifiableCredential, issuerDid, diff --git a/src/views/GiftedDetails.vue b/src/views/GiftedDetails.vue index 3e1466c..c727bdf 100644 --- a/src/views/GiftedDetails.vue +++ b/src/views/GiftedDetails.vue @@ -694,7 +694,6 @@ export default class GiftedDetails extends Vue { constructGiveParam() { const recipientDid = this.givenToRecipient ? this.recipientDid : undefined; const projectId = this.givenToProject ? this.projectId : undefined; - // const giveClaim = constructGive( const giveClaim = hydrateGive( this.prevCredToEdit?.claim as GiveVerifiableCredential, this.giverDid, diff --git a/src/views/OfferDetails.vue b/src/views/OfferDetails.vue new file mode 100644 index 0000000..f6a1225 --- /dev/null +++ b/src/views/OfferDetails.vue @@ -0,0 +1,624 @@ +