Skip to content

Examples

Contribute to Electronic Signature

Publish the Workshop Note name as an App-owned data source in the Signature template editor.

Example type Recipe

Overview

Contribute to Electronic Signature

This recipe publishes the same Workshop Note name as an App-data field that Electronic Signature documents can fill automatically. It continues the Workshop story from the earlier recipes.

What appears when you finish

ResultWhere to see it
NoteFieldsV1SignatureDocumentDataSourceWorkshop's static Signature data contract
Fail-closed Provider skeletonApp-owned location for authorized Note resolution
Note Name automatic fieldE-Signature → E-Signature Templates → Create template → Signer → Add field

Workshop Note Name App-data field

This is the actual editor after applying the generated descriptor and locale entries. Add field offers App: Workshop, Resource: Workshop Note, and Field: Note Name. Example NoteFieldsV1 value is a synthetic authoring sample, not tenant data.

Which contracts it implements and how Core receives them

The generator does not create a subclass of a Core class. It creates two classes that implement separate App SDK contracts.

Generated classSDK contractResponsibility
NoteFieldsV1SignatureDocumentDataSourceSignatureDocumentDataSourceContributionPublishes the static descriptor and runtime Provider under one owner
NoteFieldsV1SignatureDocumentDataSourceProviderSignatureDocumentDataSourceProviderReceives a query containing actor and subject context, then reauthorizes and resolves the real Note value

SignatureDocumentDataSourceContribution extends AppDescriptorContribution, so the generated contribution provides both appDescriptors() and signatureDocumentDataSourceProviders(). Core resolves the contribution through its container, allowing it to receive the generated Provider through constructor injection.

NexiaContributionRegistry discovers SignatureDocumentDataSourceContribution
  ├─ AppDescriptorCatalog collects the static source and field contract
  └─ SignatureDocumentDataSourceRegistry collects Providers
       → pairs exact appKey + sourceKey + sourceVersion and contribution ownership
       ├─ Template authoring: CatalogController returns descriptor + synthetic sample
       └─ Request preparation: RuntimeExecutor checks tenant context and contract,
                              invokes Provider::resolve(query) within a deadline,
                              then validates the returned result

The field appearing in the editor and a real Note value being filled are therefore separate stages. A static descriptor can create the authoring choice, but a fail-closed runtime Provider returns no value during request preparation.

1. Preview generation

The included script is a dry run by default:

Code example
Shell
sh docs/developers/examples/electronic-signature-contribution/commands.sh

Expect two WOULD CREATE targets:

packages/workshop/src/Descriptors/NoteFieldsV1SignatureDocumentDataSource.php
packages/workshop/src/Signature/NoteFieldsV1SignatureDocumentDataSourceProvider.php

Write them only after reviewing the preview:

Code example
Shell
WRITE=1 sh docs/developers/examples/electronic-signature-contribution/commands.sh

The command uses workshop.note as both subject and source, direct-subject lookup, cardinality one, and a string name field. The generator never infers a model or database column.

2. Add locale entries

Add the exact same keys to every locale owned by Workshop:

Code example
JSON
{
  "workshop.signature_document_data.fields.name": "Note name",
  "workshop.signature_document_data.note_fields_v1.label": "Workshop Note",
  "workshop.signature_document_data.note_fields_v1.description": "Provides the linked Note to an Electronic Signature document."
}

The descriptor's syntheticSample is only for the static authoring catalog. Never put real or sensitive data there.

3. Implement the Provider

The generated Provider intentionally returns Unavailable. Generating files must not expose real Note values. Before activation, implement all of these:

  • verify the Core-restored tenant, Legal Entity, and actor;
  • reauthorize the requested workshop.note subject ResourceRef;
  • check workshop.note.read and record-level Policy;
  • enforce the requested-field allowlist and asOf instant;
  • bound the lookup and timeout;
  • return provenance and keep values out of diagnostics.

The Provider may query the Note model inside Workshop. It must not import a Core model or another App model.

4. Understand authoring versus runtime

The Add field dialog reads only the static descriptor and synthetic sample, so the choice appears while the generated Provider still fails closed. Real values resolve only during request preparation after Core restores subject and actor context.

Template authoring                    Request preparation
Static Descriptor catalog            Runtime Provider
└─ Workshop Note                      └─ reauthorize actor and subject
   └─ Note Name · sample                 └─ return the allowed Note name

5. Refresh and inspect

Code example
Shell
task artisan -- nexia-runtime:refresh-runtime-caches --if-route-cached --no-ansi
task artisan -- nexia-apps:validate-app-descriptors workshop --no-ansi
task artisan -- nexia-apps:doctor-package-app workshop --no-ansi

After Doctor reports a paired Signature document data source, open Add field at the screenshot path and select Workshop.

Common errors

SymptomCauseFixConfirm
The command prints only WOULD CREATE and no files appearDry run is the default for both the generator and the example scriptReview the preview, then run WRITE=1 sh docs/developers/examples/electronic-signature-contribution/commands.shBoth target files exist under packages/workshop/src/
Validation reports missing locale catalog entriesThe generated descriptor label, description, and field keys were not added to every Workshop localeAdd all three keys to every resources/lang/{locale}.jsonValidation completes without a locale finding
Add field shows the choice but request preparation returns no valueThe generated Provider is a fail-closed skeleton that intentionally returns UnavailableImplement actor and subject reauthorization, the field allowlist, bounded lookup, and provenanceRequest preparation returns the allowed Note name with provenance
Doctor does not report a paired data sourceThe static descriptor and Provider differ in app key, source key, or versionMatch both identities to the same workshop source key and version, then refresh cachesDoctor reports a paired Signature document data source

Revision rules

ChangeTreatment
Label wordingChange locale values and keep the version
Compatible optional fieldConfirm consumer compatibility and review the version
Type, requiredness, or lookup meaningPublish a new source version and migrate consumers
Remove the sourceReview referencing templates and requests before lifecycle transition

Included files

Code example
Shell
#!/usr/bin/env sh
set -eu

task artisan -- nexia-apps:make-package-signature-data-source workshop NoteFieldsV1 \
  --subject-resource-key=workshop.note \
  --source-resource-key=workshop.note \
  --lookup-mode=direct_subject \
  --cardinality=one \
  --min-items=1 \
  --max-items=1 \
  --field-key=name \
  --field-type=string \
  --field-classification=internal \
  --field-formatter=plain

if [ "${WRITE:-0}" != '1' ]; then
  printf '%s\n' 'Dry run complete. Rerun with WRITE=1 to create the two fail-closed skeletons.'
  exit 0
fi

task artisan -- nexia-apps:make-package-signature-data-source workshop NoteFieldsV1 \
  --subject-resource-key=workshop.note \
  --source-resource-key=workshop.note \
  --lookup-mode=direct_subject \
  --cardinality=one \
  --min-items=1 \
  --max-items=1 \
  --field-key=name \
  --field-type=string \
  --field-classification=internal \
  --field-formatter=plain \
  --write

printf '%s\n' 'Skeletons created. Add locale labels and implement authorization and provenance before validation.'