Skip to content

Migrate template import to DID-first localId resolution #1145

@joepio

Description

@joepio

Context

With the DID migration, resources no longer live under HTTP-hierarchical paths like http://server.com/drive/website. Instead, each resource gets its own independent did:ad:<signature> subject derived from its genesis commit signature. This breaks the current localId resolution logic used during template imports.

Current behavior

When importing JSON-AD with localId properties (e.g., a website template), the server's generate_id_from_local_id() in lib/src/parse.rs constructs subjects by appending localId to the parent/importer subject:

fn generate_id_from_local_id(importer_subject: &str, local_id: &str) -> String {
    format!("{}/{}", importer_subject, local_id)
}

op
When the importer/parent is a DID-based drive (e.g., did:ad:abc123), this produces subjects like did:ad:abc123/website, which:

  1. Violates the DID-first principle (DIDs should NOT have subpaths)
  2. Creates subjects that can't be fetched via HTTP (the create-template CLI expects http://server/website)
  3. Breaks cross-references between resources within the same template

Desired behavior

Each localId-based resource imported during template application should get its own independent DID, generated from a genesis commit signature. The localId value should be persisted as a property on the resource for lookup purposes, which I think is already the case.

Scope of changes

1. Server: Two-pass import system (lib/src/parse.rs)

The import pipeline needs a two-pass approach:

Pass 1 — Create resources and collect DID mappings:

  • For each resource in the import JSON-AD, create a genesis commit
  • Sign it to derive the DID (did:ad:<genesis-signature>)
  • Store the localId as a property on the resource
  • Build a HashMap<String, String> mapping localId → DID

Pass 2 — Rewrite cross-references:

  • Walk through all AtomicUrl-typed property values
  • Replace any localId references (e.g., "website/class/page", "01j5zrevq917dp0wm4p2vnd7nr") with their resolved DID from the mapping
  • Save the final resources with correct cross-references

Key function changes:

  • generate_id_from_local_id() — remove or repurpose; no longer appends localId to parent
  • try_to_subject() — needs to check the localId → DID mapping before falling back to URL parsing
  • parse_json_ad_resource() — needs access to the mapping for reference resolution
  • pull_parents_of_props_to_front() — may need adjustment for the two-pass approach

2. Server: Import endpoint response

The /import endpoint (or the ParseOpts pipeline) should return the localId → DID mapping in its response so the frontend knows the actual subjects of imported resources.

Currently, importJsonAD in store.ts just posts to the import endpoint and expects success. It would need to receive and use the mapping to:

  • Navigate to the correct root resource after import
  • Add the correct DIDs to the drive's subResources list

3. Frontend: ApplyTemplateDialog.tsx

Currently constructs expected subjects via URL concatenation:

const subjects = template?.rootResourceLocalIDs.map(localID => {
  const base = drive.startsWith('did:') ? store.getServerUrl() : drive;
  return new URL(localID, base + '/').toString();
}) ?? stableArray;

This needs to:

  • After import, receive the localId → DID mapping from the server response
  • Use the mapping to find the DID for each rootResourceLocalID
  • Push the resolved DIDs (not constructed URLs) to the drive's subResources
  • Navigate to the resolved DID of the first root resource

4. Frontend: store.importJsonAD()

Needs to return the localId → DID mapping from the server response:

public async importJsonAD(
  jsonADString: string,
  options: ImportJsonADOptions,
): Promise<Record<string, string>> {  // localId → DID mapping
  // ...
}

5. create-template CLI (browser/create-template/)

The CLI currently hardcodes expected resource paths:

// templates.ts
ontologyID: ({ serverUrl }) => `${serverUrl}/website`,
generateEnv: ({ serverUrl }) => {
  const siteSubject = `${serverUrl}/01j5zrevq917dp0wm4p2vnd7nr`;
  // ...
}

This needs to change to query-based lookup:

  • Query the server for resources with a matching localId property under the given drive
  • Use the returned DID as the ontology subject / site resource subject
  • Alternatively, accept the ontology DID as a CLI argument (simpler)

6. Re-import / idempotency

When re-applying a template that was already imported:

  • The system should query for existing resources with matching localId values within the same parent/drive namespace
  • If found, update the existing resources rather than creating duplicates
  • The localId must be unique within its parent namespace (enforced by query)

7. Website template data (browser/data-browser/src/components/Template/templates/website.tsx)

The template definition uses localId strings as cross-references between resources:

{
  [core.properties.localId]: 'site-data',
  [dataBrowser.properties.subResources]: [
    '01j5zrd23mxam4mdg2ak97gqcm',  // ← these are localId refs
    '01j5zrevq917dp0wm4p2vnd7nr',
    // ...
  ],
}

These cross-references need to be resolved during the two-pass import. The template data itself doesn't need to change — the import system handles the rewriting.

8. Tests

  • browser/e2e/tests/template.spec.ts — currently skipped with test.describe.skip(). Re-enable after implementation.
  • lib/src/parse.rs tests — update existing import_resource_with_localid, import_resource_with_json, references_to_localid_resources tests to verify DID generation instead of path concatenation.

Implementation order (suggested)

  1. Server parse.rs — implement two-pass import with localId → DID mapping
  2. Server import endpoint — return the mapping in the response
  3. Update parse.rs tests
  4. Frontend store.importJsonAD() — parse and return the mapping
  5. Frontend ApplyTemplateDialog — use mapping for navigation and subResources
  6. create-template CLI — switch to query-based or DID-argument lookup
  7. Re-enable and update template.spec.ts e2e tests
  8. Handle re-import idempotency

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions