docs(testing): add PlanAction JWT hydration implementation guide
Add comprehensive implementation guide for creating PlanAction claims via hydratePlan() function pattern, following established hydrateGive() and hydrateOffer() patterns. Changes: - Add hydrate-plan-implementation-guide.md with complete implementation details, usage examples, and testing recommendations - Link implementation guide from getting-valid-plan-ids.md Method 6 - Link implementation guide from localhost-testing-guide.md Option B The guide provides: - Complete hydratePlan() function implementation - PlanActionClaim interface structure - Helper functions (createAndSubmitPlan, editAndSubmitPlan) - Usage examples and edge cases - Testing recommendations and security considerations This complements plan creation documentation by showing how to programmatically construct valid PlanAction JWTs for POST /api/v2/claim.
This commit is contained in:
@@ -78,6 +78,12 @@ Plans are created by importing JWT claims containing a `PlanAction`:
|
|||||||
- JWT must be signed with a valid DID key
|
- JWT must be signed with a valid DID key
|
||||||
- Response includes `handleId` and `planId` if creation succeeds
|
- Response includes `handleId` and `planId` if creation succeeds
|
||||||
|
|
||||||
|
**Implementation Guide**: See [`docs/hydrate-plan-implementation-guide.md`](./hydrate-plan-implementation-guide.md) for complete implementation details, including:
|
||||||
|
- `hydratePlan()` function pattern
|
||||||
|
- PlanActionClaim interface structure
|
||||||
|
- Helper functions for create/edit operations
|
||||||
|
- Usage examples and testing recommendations
|
||||||
|
|
||||||
**Note**: This method requires generating signed JWTs with DID keys, which is complex. **Method 1 (TimeSafari App UI) is recommended** for creating test plans.
|
**Note**: This method requires generating signed JWTs with DID keys, which is complex. **Method 1 (TimeSafari App UI) is recommended** for creating test plans.
|
||||||
|
|
||||||
## Plan Handle ID Format
|
## Plan Handle ID Format
|
||||||
|
|||||||
564
docs/hydrate-plan-implementation-guide.md
Normal file
564
docs/hydrate-plan-implementation-guide.md
Normal file
@@ -0,0 +1,564 @@
|
|||||||
|
# Directive: hydratePlan Implementation Guide
|
||||||
|
|
||||||
|
**Author**: Matthew Raymer
|
||||||
|
**Date**: 2025-10-29 12:38:40 UTC
|
||||||
|
**Status**: 🎯 **ACTIVE** - Implementation directive for PlanActionClaim hydration
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This directive provides a complete implementation guide for creating a
|
||||||
|
`hydratePlan()` function that follows the same pattern as `hydrateGive()` and
|
||||||
|
`hydrateOffer()`. This function constructs and hydrates `PlanActionClaim`
|
||||||
|
entities (projects) for submission to the endorser server.
|
||||||
|
|
||||||
|
## Pattern Reference
|
||||||
|
|
||||||
|
The `hydratePlan()` function should follow the established pattern from:
|
||||||
|
|
||||||
|
- **`hydrateGive()`** (`src/libs/endorserServer.ts:831-920`)
|
||||||
|
- **`hydrateOffer()`** (`src/libs/endorserServer.ts:1018-1072`)
|
||||||
|
|
||||||
|
### Key Pattern Elements
|
||||||
|
|
||||||
|
1. **Function Signature**: Accepts optional original claim and parameters
|
||||||
|
2. **Clone or Create**: Clone existing claim or create new base structure
|
||||||
|
3. **Edit Detection**: Handle `lastClaimId` for edit operations
|
||||||
|
4. **Field Hydration**: Set optional fields only when provided
|
||||||
|
5. **Cleanup**: Use `undefined` to remove fields (not empty strings)
|
||||||
|
6. **Return**: Return properly typed claim object
|
||||||
|
|
||||||
|
## PlanActionClaim Interface
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface PlanActionClaim extends ClaimObject {
|
||||||
|
"@context": "https://schema.org";
|
||||||
|
"@type": "PlanAction";
|
||||||
|
name: string; // REQUIRED
|
||||||
|
agent?: { identifier: string }; // Optional: creator DID
|
||||||
|
description?: string; // Optional: project description
|
||||||
|
endTime?: string; // Optional: ISO 8601 date string
|
||||||
|
identifier?: string; // Auto-set by server on first create
|
||||||
|
image?: string; // Optional: image URL
|
||||||
|
lastClaimId?: string; // Required for edits
|
||||||
|
location?: { // Optional: geographic location
|
||||||
|
geo: {
|
||||||
|
"@type": "GeoCoordinates";
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
startTime?: string; // Optional: ISO 8601 date string
|
||||||
|
url?: string; // Optional: project URL
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
### Function Signature
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* Construct PlanAction VC for submission to server
|
||||||
|
*
|
||||||
|
* Creates or updates a PlanAction claim (project) with all optional fields
|
||||||
|
* properly handled. Follows the same pattern as hydrateGive() and
|
||||||
|
* hydrateOffer().
|
||||||
|
*
|
||||||
|
* @param vcClaimOrig - Optional existing claim to clone and modify
|
||||||
|
* @param name - Required project name
|
||||||
|
* @param agentDid - Optional DID of the project creator
|
||||||
|
* @param description - Optional project description
|
||||||
|
* @param startTime - Optional ISO 8601 start date/time string
|
||||||
|
* @param endTime - Optional ISO 8601 end date/time string
|
||||||
|
* @param imageUrl - Optional image URL for the project
|
||||||
|
* @param url - Optional project URL
|
||||||
|
* @param latitude - Optional location latitude
|
||||||
|
* @param longitude - Optional location longitude
|
||||||
|
* @param lastClaimId - Required when editing an existing project
|
||||||
|
*
|
||||||
|
* @returns Properly hydrated PlanActionClaim ready for submission
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Create new project
|
||||||
|
* const claim = hydratePlan(
|
||||||
|
* undefined,
|
||||||
|
* "Community Garden Project",
|
||||||
|
* "did:ethr:0x123...",
|
||||||
|
* "Building a community garden",
|
||||||
|
* "2025-06-01T00:00:00Z",
|
||||||
|
* "2025-08-31T23:59:59Z"
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Edit existing project
|
||||||
|
* const updated = hydratePlan(
|
||||||
|
* existingClaim,
|
||||||
|
* "Updated Project Name",
|
||||||
|
* "did:ethr:0x123...",
|
||||||
|
* "New description",
|
||||||
|
* undefined,
|
||||||
|
* undefined,
|
||||||
|
* undefined,
|
||||||
|
* undefined,
|
||||||
|
* undefined,
|
||||||
|
* undefined,
|
||||||
|
* "01ABC123..." // lastClaimId
|
||||||
|
* );
|
||||||
|
*/
|
||||||
|
export function hydratePlan(
|
||||||
|
vcClaimOrig?: PlanActionClaim,
|
||||||
|
name?: string,
|
||||||
|
agentDid?: string,
|
||||||
|
description?: string,
|
||||||
|
startTime?: string,
|
||||||
|
endTime?: string,
|
||||||
|
imageUrl?: string,
|
||||||
|
url?: string,
|
||||||
|
latitude?: number,
|
||||||
|
longitude?: number,
|
||||||
|
lastClaimId?: string,
|
||||||
|
): PlanActionClaim {
|
||||||
|
// Clone existing claim or create base structure
|
||||||
|
const vcClaim: PlanActionClaim = vcClaimOrig
|
||||||
|
? R.clone(vcClaimOrig)
|
||||||
|
: {
|
||||||
|
"@context": SCHEMA_ORG_CONTEXT,
|
||||||
|
"@type": "PlanAction",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle edit operation (lastClaimId indicates edit)
|
||||||
|
if (lastClaimId) {
|
||||||
|
vcClaim.lastClaimId = lastClaimId;
|
||||||
|
// Remove identifier on edit - server assigns new one
|
||||||
|
delete vcClaim.identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set required name field (must be provided for new claims)
|
||||||
|
if (name) {
|
||||||
|
vcClaim.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle agent (creator DID)
|
||||||
|
if (agentDid) {
|
||||||
|
vcClaim.agent = { identifier: agentDid };
|
||||||
|
} else {
|
||||||
|
// Remove agent if not provided (allows clearing)
|
||||||
|
delete vcClaim.agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle description
|
||||||
|
vcClaim.description = description || undefined;
|
||||||
|
|
||||||
|
// Handle start time (ISO 8601 format required)
|
||||||
|
if (startTime) {
|
||||||
|
// Validate it's a proper ISO 8601 string
|
||||||
|
try {
|
||||||
|
const date = new Date(startTime);
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
throw new Error("Invalid startTime format");
|
||||||
|
}
|
||||||
|
vcClaim.startTime = startTime;
|
||||||
|
} catch {
|
||||||
|
// Invalid date - remove the field
|
||||||
|
delete vcClaim.startTime;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete vcClaim.startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle end time (ISO 8601 format required)
|
||||||
|
if (endTime) {
|
||||||
|
try {
|
||||||
|
const date = new Date(endTime);
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
throw new Error("Invalid endTime format");
|
||||||
|
}
|
||||||
|
vcClaim.endTime = endTime;
|
||||||
|
} catch {
|
||||||
|
delete vcClaim.endTime;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete vcClaim.endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle image URL
|
||||||
|
vcClaim.image = imageUrl || undefined;
|
||||||
|
|
||||||
|
// Handle project URL
|
||||||
|
vcClaim.url = url || undefined;
|
||||||
|
|
||||||
|
// Handle location (both latitude and longitude required)
|
||||||
|
if (latitude !== undefined && longitude !== undefined) {
|
||||||
|
// Validate coordinates are numbers
|
||||||
|
if (
|
||||||
|
typeof latitude === "number" &&
|
||||||
|
typeof longitude === "number" &&
|
||||||
|
!isNaN(latitude) &&
|
||||||
|
!isNaN(longitude)
|
||||||
|
) {
|
||||||
|
vcClaim.location = {
|
||||||
|
geo: {
|
||||||
|
"@type": "GeoCoordinates",
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Invalid coordinates - remove location
|
||||||
|
delete vcClaim.location;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Remove location if either coordinate missing
|
||||||
|
delete vcClaim.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vcClaim;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Helper Functions (Recommended)
|
||||||
|
|
||||||
|
Following the pattern from `createAndSubmitGive()` and
|
||||||
|
`createAndSubmitOffer()`, create wrapper functions:
|
||||||
|
|
||||||
|
### Create and Submit
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* Create and submit a new PlanAction claim
|
||||||
|
*
|
||||||
|
* @param axios - Axios instance for HTTP requests
|
||||||
|
* @param apiServer - Endorser API server URL
|
||||||
|
* @param issuerDid - DID to sign the claim (must match agentDid or
|
||||||
|
* be authorized)
|
||||||
|
* @param name - Required project name
|
||||||
|
* @param agentDid - Optional creator DID
|
||||||
|
* @param description - Optional project description
|
||||||
|
* @param startTime - Optional ISO 8601 start time
|
||||||
|
* @param endTime - Optional ISO 8601 end time
|
||||||
|
* @param imageUrl - Optional image URL
|
||||||
|
* @param url - Optional project URL
|
||||||
|
* @param latitude - Optional location latitude
|
||||||
|
* @param longitude - Optional location longitude
|
||||||
|
*
|
||||||
|
* @returns Promise with submission result (handleId, claimId, etc.)
|
||||||
|
*/
|
||||||
|
export async function createAndSubmitPlan(
|
||||||
|
axios: Axios,
|
||||||
|
apiServer: string,
|
||||||
|
issuerDid: string,
|
||||||
|
name: string,
|
||||||
|
agentDid?: string,
|
||||||
|
description?: string,
|
||||||
|
startTime?: string,
|
||||||
|
endTime?: string,
|
||||||
|
imageUrl?: string,
|
||||||
|
url?: string,
|
||||||
|
latitude?: number,
|
||||||
|
longitude?: number,
|
||||||
|
): Promise<CreateAndSubmitClaimResult> {
|
||||||
|
const vcClaim = hydratePlan(
|
||||||
|
undefined,
|
||||||
|
name,
|
||||||
|
agentDid,
|
||||||
|
description,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
imageUrl,
|
||||||
|
url,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
undefined, // No lastClaimId for new claims
|
||||||
|
);
|
||||||
|
return createAndSubmitClaim(
|
||||||
|
vcClaim as GenericVerifiableCredential,
|
||||||
|
issuerDid,
|
||||||
|
apiServer,
|
||||||
|
axios,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Edit and Submit
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* Edit and submit an existing PlanAction claim
|
||||||
|
*
|
||||||
|
* @param axios - Axios instance for HTTP requests
|
||||||
|
* @param apiServer - Endorser API server URL
|
||||||
|
* @param fullClaim - Existing claim wrapper with ID
|
||||||
|
* @param issuerDid - DID to sign the claim
|
||||||
|
* @param name - Updated project name (if changed)
|
||||||
|
* @param agentDid - Updated creator DID (if changed)
|
||||||
|
* @param description - Updated description (if changed)
|
||||||
|
* @param startTime - Updated start time (if changed)
|
||||||
|
* @param endTime - Updated end time (if changed)
|
||||||
|
* @param imageUrl - Updated image URL (if changed)
|
||||||
|
* @param url - Updated project URL (if changed)
|
||||||
|
* @param latitude - Updated location latitude (if changed)
|
||||||
|
* @param longitude - Updated location longitude (if changed)
|
||||||
|
*
|
||||||
|
* @returns Promise with submission result
|
||||||
|
*/
|
||||||
|
export async function editAndSubmitPlan(
|
||||||
|
axios: Axios,
|
||||||
|
apiServer: string,
|
||||||
|
fullClaim: GenericCredWrapper<PlanActionClaim>,
|
||||||
|
issuerDid: string,
|
||||||
|
name?: string,
|
||||||
|
agentDid?: string,
|
||||||
|
description?: string,
|
||||||
|
startTime?: string,
|
||||||
|
endTime?: string,
|
||||||
|
imageUrl?: string,
|
||||||
|
url?: string,
|
||||||
|
latitude?: number,
|
||||||
|
longitude?: number,
|
||||||
|
): Promise<CreateAndSubmitClaimResult> {
|
||||||
|
const vcClaim = hydratePlan(
|
||||||
|
fullClaim.claim,
|
||||||
|
name,
|
||||||
|
agentDid,
|
||||||
|
description,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
imageUrl,
|
||||||
|
url,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
fullClaim.id, // Use existing claim ID for edit
|
||||||
|
);
|
||||||
|
return createAndSubmitClaim(
|
||||||
|
vcClaim as GenericVerifiableCredential,
|
||||||
|
issuerDid,
|
||||||
|
apiServer,
|
||||||
|
axios,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required Dependencies
|
||||||
|
|
||||||
|
Ensure these imports are available:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import * as R from "ramda"; // For R.clone()
|
||||||
|
import { Axios } from "axios";
|
||||||
|
import { PlanActionClaim } from "../interfaces/claims";
|
||||||
|
import {
|
||||||
|
GenericCredWrapper,
|
||||||
|
GenericVerifiableCredential,
|
||||||
|
CreateAndSubmitClaimResult
|
||||||
|
} from "../interfaces/common";
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
export const SCHEMA_ORG_CONTEXT = "https://schema.org";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Example 1: Create Simple Project
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const claim = hydratePlan(
|
||||||
|
undefined, // New claim
|
||||||
|
"My Project", // Required name
|
||||||
|
"did:ethr:0x123...", // Creator DID
|
||||||
|
"Project description" // Description
|
||||||
|
);
|
||||||
|
|
||||||
|
// Result:
|
||||||
|
// {
|
||||||
|
// "@context": "https://schema.org",
|
||||||
|
// "@type": "PlanAction",
|
||||||
|
// "name": "My Project",
|
||||||
|
// "agent": { "identifier": "did:ethr:0x123..." },
|
||||||
|
// "description": "Project description"
|
||||||
|
// }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Create Project with Dates and Location
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const claim = hydratePlan(
|
||||||
|
undefined,
|
||||||
|
"Community Event",
|
||||||
|
"did:ethr:0x456...",
|
||||||
|
"Annual community gathering",
|
||||||
|
"2025-07-01T10:00:00Z", // Start time
|
||||||
|
"2025-07-03T18:00:00Z", // End time
|
||||||
|
"https://example.com/img.jpg", // Image
|
||||||
|
"https://example.com/event", // URL
|
||||||
|
40.7128, // Latitude
|
||||||
|
-74.0060 // Longitude
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Edit Existing Project
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const existingClaim: PlanActionClaim = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "PlanAction",
|
||||||
|
"name": "Old Name",
|
||||||
|
"identifier": "01ABC123...",
|
||||||
|
// ... other fields
|
||||||
|
};
|
||||||
|
|
||||||
|
const updated = hydratePlan(
|
||||||
|
existingClaim, // Original claim to clone
|
||||||
|
"New Name", // Updated name
|
||||||
|
"did:ethr:0x123...", // Keep same agent
|
||||||
|
"Updated description", // Updated description
|
||||||
|
undefined, // Keep existing startTime
|
||||||
|
undefined, // Keep existing endTime
|
||||||
|
undefined, // Keep existing image
|
||||||
|
undefined, // Keep existing url
|
||||||
|
undefined, // Keep existing location
|
||||||
|
undefined, // Keep existing location
|
||||||
|
"01ABC123..." // Required: lastClaimId
|
||||||
|
);
|
||||||
|
|
||||||
|
// Result will have:
|
||||||
|
// - lastClaimId: "01ABC123..."
|
||||||
|
// - identifier: undefined (removed for edit)
|
||||||
|
// - Updated name and description
|
||||||
|
// - All other fields preserved from original
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: Remove Location from Existing Project
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const updated = hydratePlan(
|
||||||
|
existingClaim,
|
||||||
|
existingClaim.name, // Keep name
|
||||||
|
existingClaim.agent?.identifier,
|
||||||
|
existingClaim.description,
|
||||||
|
existingClaim.startTime,
|
||||||
|
existingClaim.endTime,
|
||||||
|
existingClaim.image,
|
||||||
|
existingClaim.url,
|
||||||
|
undefined, // latitude undefined = remove location
|
||||||
|
undefined, // longitude undefined = remove location
|
||||||
|
"01ABC123..."
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
### Date/Time Handling
|
||||||
|
|
||||||
|
- **Format**: All date/time strings must be ISO 8601 format
|
||||||
|
- **Validation**: Function validates date strings but caller should ensure
|
||||||
|
proper format
|
||||||
|
- **Timezone**: Include timezone information (Z for UTC, or +/- offset)
|
||||||
|
- **Example**: `"2025-06-01T10:00:00Z"` or `"2025-06-01T10:00:00-05:00"`
|
||||||
|
|
||||||
|
### Location Handling
|
||||||
|
|
||||||
|
- **Both Required**: Both `latitude` and `longitude` must be provided
|
||||||
|
together, or both omitted
|
||||||
|
- **Validation**: Coordinates must be valid numbers (not NaN)
|
||||||
|
- **Removal**: Setting either to `undefined` removes the entire location
|
||||||
|
object
|
||||||
|
- **Range**: Latitude: -90 to 90, Longitude: -180 to 180 (validation
|
||||||
|
recommended)
|
||||||
|
|
||||||
|
### Edit Operations
|
||||||
|
|
||||||
|
- **lastClaimId Required**: Must provide `lastClaimId` when editing
|
||||||
|
- **identifier Removed**: Server assigns new identifier on edit
|
||||||
|
- **Field Preservation**: Only explicitly changed fields are modified;
|
||||||
|
others preserved from original
|
||||||
|
- **Undefined vs Missing**: Use `undefined` to explicitly remove optional
|
||||||
|
fields
|
||||||
|
|
||||||
|
### Field Cleanup
|
||||||
|
|
||||||
|
- **Use undefined**: Set fields to `undefined` rather than empty strings
|
||||||
|
- **Delete for Edit**: For edits, explicitly delete fields that should be
|
||||||
|
removed
|
||||||
|
- **Optional Field Pattern**: Check existence before setting to preserve
|
||||||
|
optional nature
|
||||||
|
|
||||||
|
## Testing Recommendations
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
Test the following scenarios:
|
||||||
|
|
||||||
|
1. **New Claim Creation**
|
||||||
|
- Minimum required fields (name only)
|
||||||
|
- All optional fields provided
|
||||||
|
- Validate structure matches interface
|
||||||
|
|
||||||
|
2. **Edit Operations**
|
||||||
|
- Clone existing claim correctly
|
||||||
|
- lastClaimId sets identifier removal
|
||||||
|
- Partial field updates preserve other fields
|
||||||
|
|
||||||
|
3. **Field Validation**
|
||||||
|
- Invalid date strings removed
|
||||||
|
- Location requires both coordinates
|
||||||
|
- Undefined fields properly handled
|
||||||
|
|
||||||
|
4. **Edge Cases**
|
||||||
|
- Empty string handling (should become undefined)
|
||||||
|
- NaN coordinate values
|
||||||
|
- Missing required fields on new claims
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
|
||||||
|
Test with actual server submission:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Test createAndSubmitPlan
|
||||||
|
const result = await createAndSubmitPlan(
|
||||||
|
axiosInstance,
|
||||||
|
"https://test-api.endorser.ch",
|
||||||
|
testDid,
|
||||||
|
"Test Project",
|
||||||
|
testDid,
|
||||||
|
"Test description"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.handleId).toBeDefined();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
1. **DID Validation**: Validate agentDid matches issuerDid or is authorized
|
||||||
|
2. **Date Validation**: Ensure date strings don't contain injection attempts
|
||||||
|
3. **URL Validation**: Validate image and URL fields are proper URLs
|
||||||
|
4. **Coordinate Validation**: Ensure lat/lng are within valid ranges
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- **hydrateGive()**: `src/libs/endorserServer.ts:831-920`
|
||||||
|
- **hydrateOffer()**: `src/libs/endorserServer.ts:1018-1072`
|
||||||
|
- **PlanActionClaim Interface**: `src/interfaces/claims.ts:76-91`
|
||||||
|
- **Pattern Example**: `src/views/NewEditProjectView.vue:536-597`
|
||||||
|
|
||||||
|
## Implementation Checklist
|
||||||
|
|
||||||
|
- [ ] Implement `hydratePlan()` function with all parameters
|
||||||
|
- [ ] Add date/time validation logic
|
||||||
|
- [ ] Add location coordinate validation
|
||||||
|
- [ ] Implement `createAndSubmitPlan()` wrapper
|
||||||
|
- [ ] Implement `editAndSubmitPlan()` wrapper
|
||||||
|
- [ ] Add comprehensive JSDoc comments
|
||||||
|
- [ ] Write unit tests for all scenarios
|
||||||
|
- [ ] Write integration tests with server
|
||||||
|
- [ ] Update any existing code to use new function
|
||||||
|
- [ ] Document breaking changes if refactoring existing code
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status**: Active implementation directive
|
||||||
|
**Priority**: Medium
|
||||||
|
**Estimated Effort**: 2-4 hours for complete implementation including tests
|
||||||
|
**Dependencies**: Requires ramda, axios, and existing claim interfaces
|
||||||
|
**Stakeholders**: Development team implementing claim hydration utilities
|
||||||
|
|
||||||
@@ -70,6 +70,8 @@ If you have your own localhost API server, you must create plans first. Plans ar
|
|||||||
|
|
||||||
**Note**: Creating PlanAction JWTs requires DID signing and proper claim structure, which is complex. For quick testing, **Option A (test API server) is recommended**.
|
**Note**: Creating PlanAction JWTs requires DID signing and proper claim structure, which is complex. For quick testing, **Option A (test API server) is recommended**.
|
||||||
|
|
||||||
|
**Implementation Reference**: If you need to programmatically create PlanAction JWTs, see [`docs/hydrate-plan-implementation-guide.md`](./hydrate-plan-implementation-guide.md) for the complete implementation pattern, including `hydratePlan()` function and helper utilities.
|
||||||
|
|
||||||
Then verify your setup:
|
Then verify your setup:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Reference in New Issue
Block a user