feat(core): add provider-neutral records filing contract
This commit is contained in:
@@ -17,6 +17,7 @@ operator, and roadmap pages.
|
|||||||
| Action/effect automation layer | `ACTION_EFFECT_AUTOMATION_LAYER.md` | Action/effect contracts, consequence preview, runner semantics, and module boundary for automation. |
|
| Action/effect automation layer | `ACTION_EFFECT_AUTOMATION_LAYER.md` | Action/effect contracts, consequence preview, runner semantics, and module boundary for automation. |
|
||||||
| External references and integration maturity | `EXTERNAL_REFERENCES_AND_INTEGRATION_MATURITY.md` | Stable external identity and cumulative connector maturity; configured source authority is defined by the meta target architecture. |
|
| External references and integration maturity | `EXTERNAL_REFERENCES_AND_INTEGRATION_MATURITY.md` | Stable external identity and cumulative connector maturity; configured source authority is defined by the meta target architecture. |
|
||||||
| Institutional context and governed references | `INSTITUTIONAL_CONTEXT_CONTRACT.md` | Shared temporal, actor/representation, institution, mandate, service, party, decision, evidence, legal-basis, information-governance, presentation, and geo DTO/provider contracts. |
|
| Institutional context and governed references | `INSTITUTIONAL_CONTEXT_CONTRACT.md` | Shared temporal, actor/representation, institution, mandate, service, party, decision, evidence, legal-basis, information-governance, presentation, and geo DTO/provider contracts. |
|
||||||
|
| Provider-neutral record filing | `RECORDS_FILING_CONTRACT.md` | Exact source-revision identity, current source authorization, idempotent filing, capability discovery, and ownership boundary. |
|
||||||
| Temporal data read context | `TEMPORAL_DATA_CONTEXT.md` | Valid-time and recorded-time titlebar selection, HTTP/cache contract, security boundary, and module-adoption rule. |
|
| Temporal data read context | `TEMPORAL_DATA_CONTEXT.md` | Valid-time and recorded-time titlebar selection, HTTP/cache contract, security boundary, and module-adoption rule. |
|
||||||
| Cross-module information governance adoption | `INFORMATION_GOVERNANCE_ADOPTION.md` | Manifest evidence and enforcement rules for temporal browsing, purpose-aware access, retention, and institutional context. |
|
| Cross-module information governance adoption | `INFORMATION_GOVERNANCE_ADOPTION.md` | Manifest evidence and enforcement rules for temporal browsing, purpose-aware access, retention, and institutional context. |
|
||||||
| Context-sensitive F1 help | `CONTEXTUAL_HELP_CONTRACT.md` | Focus, route, module-manifest documentation contexts, Docs projection, and hosted fallback. |
|
| Context-sensitive F1 help | `CONTEXTUAL_HELP_CONTRACT.md` | Focus, route, module-manifest documentation contexts, Docs projection, and hosted fallback. |
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# Records Filing Contract
|
||||||
|
|
||||||
|
Core exposes a small provider-neutral contract for filing exact source
|
||||||
|
revisions into an institutional record. Core does not own records semantics,
|
||||||
|
source-object authorization, or source bytes. `govoplan-records` owns filing
|
||||||
|
orchestration and chronology; each source module owns resolution of its exact
|
||||||
|
revision.
|
||||||
|
|
||||||
|
## Capability Names
|
||||||
|
|
||||||
|
- `records.filing` is supplied by the enabled Records module.
|
||||||
|
- `records.source.<module>` is supplied by an enabled source module, for
|
||||||
|
example `records.source.files` or `records.source.cases`.
|
||||||
|
|
||||||
|
Callers discover capabilities through the module registry. They must not
|
||||||
|
import optional source-module internals.
|
||||||
|
|
||||||
|
## Exact Source Identity
|
||||||
|
|
||||||
|
`RecordSourceLocator` identifies one tenant, source module, resource type,
|
||||||
|
resource ID, and immutable source revision. A source provider must:
|
||||||
|
|
||||||
|
1. reject cross-tenant resolution;
|
||||||
|
2. require a non-empty purpose;
|
||||||
|
3. re-evaluate the caller's current module and object authorization;
|
||||||
|
4. resolve exactly the requested revision, never a mutable "current" alias;
|
||||||
|
5. return safe display/provenance metadata and a SHA-256 digest when the source
|
||||||
|
has stable bytes or a canonical snapshot;
|
||||||
|
6. fail closed when the revision is missing, quarantined, corrupt, or no longer
|
||||||
|
authorized.
|
||||||
|
|
||||||
|
Historical Records browsing never revives historical access rights. The
|
||||||
|
source's current authorization decision remains authoritative when filing.
|
||||||
|
|
||||||
|
## Filing Semantics
|
||||||
|
|
||||||
|
`RecordFilingRequest` binds the exact source to a record, purpose, filing
|
||||||
|
reason, relationship, institutional context, and idempotency key. Records must
|
||||||
|
persist source identity and resolution evidence together with the filing actor,
|
||||||
|
represented capacity, valid time, recorded time, and immutable chronology.
|
||||||
|
|
||||||
|
An idempotency key may replay only an identical request. A conflicting reuse
|
||||||
|
must fail. Filing does not transfer ownership of source content and must not
|
||||||
|
silently copy mutable source state.
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
The Python DTOs and protocols live in `govoplan_core.core.records`. The
|
||||||
|
manifest interface `records.filing` starts at `1.0.0`. Incompatible DTO or
|
||||||
|
behavior changes require a new interface version and release impact analysis;
|
||||||
|
additional optional metadata remains backward compatible.
|
||||||
|
|
||||||
|
## Initial Providers
|
||||||
|
|
||||||
|
- Files resolves an exact managed `FileVersion`, verifies current Files access
|
||||||
|
and blob integrity, and returns its stored content digest.
|
||||||
|
- Cases resolves an exact immutable case revision after current case access and
|
||||||
|
returns a digest of the canonical revision snapshot.
|
||||||
|
|
||||||
|
Provider-specific selection UI belongs to the source module. The generic
|
||||||
|
Records dialog remains a diagnostic/manual fallback for exact identifiers.
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Mapping, Sequence
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Literal, Protocol, runtime_checkable
|
||||||
|
|
||||||
|
|
||||||
|
CAPABILITY_RECORDS_FILING = "records.filing"
|
||||||
|
CAPABILITY_RECORD_SOURCE_PREFIX = "records.source."
|
||||||
|
|
||||||
|
RecordSourceAuthority = Literal[
|
||||||
|
"native_authoritative",
|
||||||
|
"external_authoritative",
|
||||||
|
"external_mirror",
|
||||||
|
"governed_sync",
|
||||||
|
"governance_overlay",
|
||||||
|
"linked_reference",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RecordContractError(ValueError):
|
||||||
|
"""Stable error for provider-neutral record filing operations."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class RecordSourceLocator:
|
||||||
|
"""Exact source revision requested for filing into a record."""
|
||||||
|
|
||||||
|
tenant_id: str
|
||||||
|
source_module: str
|
||||||
|
resource_type: str
|
||||||
|
resource_id: str
|
||||||
|
source_revision: str
|
||||||
|
metadata: Mapping[str, object] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
_require_text_fields(
|
||||||
|
self,
|
||||||
|
"tenant_id",
|
||||||
|
"source_module",
|
||||||
|
"resource_type",
|
||||||
|
"resource_id",
|
||||||
|
"source_revision",
|
||||||
|
)
|
||||||
|
if len(self.resource_id) > 500 or len(self.source_revision) > 255:
|
||||||
|
raise RecordContractError("Record source identity is too long.")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class RecordSourceReference:
|
||||||
|
"""Provider-resolved immutable source metadata safe to preserve in Records."""
|
||||||
|
|
||||||
|
locator: RecordSourceLocator
|
||||||
|
label: str
|
||||||
|
authority_mode: RecordSourceAuthority = "linked_reference"
|
||||||
|
content_sha256: str | None = None
|
||||||
|
content_type: str | None = None
|
||||||
|
size_bytes: int | None = None
|
||||||
|
valid_from: datetime | None = None
|
||||||
|
valid_to: datetime | None = None
|
||||||
|
recorded_at: datetime | None = None
|
||||||
|
launch_url: str | None = None
|
||||||
|
metadata: Mapping[str, object] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
if not self.label.strip():
|
||||||
|
raise RecordContractError("Record source references require a label.")
|
||||||
|
if len(self.label) > 500:
|
||||||
|
raise RecordContractError(
|
||||||
|
"Record source labels are limited to 500 characters."
|
||||||
|
)
|
||||||
|
if self.size_bytes is not None and self.size_bytes < 0:
|
||||||
|
raise RecordContractError("Record source sizes cannot be negative.")
|
||||||
|
if self.content_sha256 is not None:
|
||||||
|
digest = self.content_sha256.removeprefix("sha256:")
|
||||||
|
if len(digest) != 64 or any(
|
||||||
|
character not in "0123456789abcdefABCDEF" for character in digest
|
||||||
|
):
|
||||||
|
raise RecordContractError(
|
||||||
|
"Record source SHA-256 digests must be hexadecimal."
|
||||||
|
)
|
||||||
|
if self.valid_from and self.valid_to and self.valid_to <= self.valid_from:
|
||||||
|
raise RecordContractError(
|
||||||
|
"Record source valid_to must be after valid_from."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class RecordFilingRequest:
|
||||||
|
tenant_id: str
|
||||||
|
record_id: str
|
||||||
|
source: RecordSourceLocator
|
||||||
|
purpose: str
|
||||||
|
filing_reason: str
|
||||||
|
idempotency_key: str
|
||||||
|
volume_id: str | None = None
|
||||||
|
relationship: str = "contains"
|
||||||
|
institutional_context: Mapping[str, object] = field(default_factory=dict)
|
||||||
|
metadata: Mapping[str, object] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
_require_text_fields(
|
||||||
|
self,
|
||||||
|
"tenant_id",
|
||||||
|
"record_id",
|
||||||
|
"purpose",
|
||||||
|
"filing_reason",
|
||||||
|
"idempotency_key",
|
||||||
|
"relationship",
|
||||||
|
)
|
||||||
|
if self.source.tenant_id != self.tenant_id:
|
||||||
|
raise RecordContractError("Record filing cannot cross tenants.")
|
||||||
|
if len(self.purpose) > 255 or len(self.filing_reason) > 2_000:
|
||||||
|
raise RecordContractError("Record filing purpose or reason is too long.")
|
||||||
|
if len(self.idempotency_key) > 255:
|
||||||
|
raise RecordContractError(
|
||||||
|
"Record filing idempotency keys are limited to 255 characters."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class RecordFilingResult:
|
||||||
|
record_id: str
|
||||||
|
item_id: str
|
||||||
|
sequence: int
|
||||||
|
source: RecordSourceReference
|
||||||
|
filed_at: datetime
|
||||||
|
replayed: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class RecordSourceProvider(Protocol):
|
||||||
|
provider_id: str
|
||||||
|
|
||||||
|
def resource_types(self) -> Sequence[str]: ...
|
||||||
|
|
||||||
|
def resolve(
|
||||||
|
self,
|
||||||
|
session: object,
|
||||||
|
principal: object,
|
||||||
|
*,
|
||||||
|
locator: RecordSourceLocator,
|
||||||
|
purpose: str,
|
||||||
|
) -> RecordSourceReference:
|
||||||
|
"""Resolve one currently authorized, exact source revision."""
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class RecordFilingService(Protocol):
|
||||||
|
def file(
|
||||||
|
self,
|
||||||
|
session: object,
|
||||||
|
principal: object,
|
||||||
|
*,
|
||||||
|
request: RecordFilingRequest,
|
||||||
|
) -> RecordFilingResult: ...
|
||||||
|
|
||||||
|
|
||||||
|
def record_source_capability(source_module: str) -> str:
|
||||||
|
normalized = source_module.strip().lower()
|
||||||
|
if not normalized or any(
|
||||||
|
character not in "abcdefghijklmnopqrstuvwxyz0123456789_-"
|
||||||
|
for character in normalized
|
||||||
|
):
|
||||||
|
raise RecordContractError("Record source module identifiers are invalid.")
|
||||||
|
return f"{CAPABILITY_RECORD_SOURCE_PREFIX}{normalized}"
|
||||||
|
|
||||||
|
|
||||||
|
def record_source_capabilities(registry: object | None) -> tuple[str, ...]:
|
||||||
|
if registry is None or not hasattr(registry, "capability_names"):
|
||||||
|
return ()
|
||||||
|
return tuple(
|
||||||
|
name
|
||||||
|
for name in registry.capability_names()
|
||||||
|
if str(name).startswith(CAPABILITY_RECORD_SOURCE_PREFIX)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _require_text_fields(value: object, *field_names: str) -> None:
|
||||||
|
for field_name in field_names:
|
||||||
|
if not str(getattr(value, field_name, "") or "").strip():
|
||||||
|
raise RecordContractError(
|
||||||
|
f"Record contract field {field_name} is required."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"CAPABILITY_RECORDS_FILING",
|
||||||
|
"CAPABILITY_RECORD_SOURCE_PREFIX",
|
||||||
|
"RecordContractError",
|
||||||
|
"RecordFilingRequest",
|
||||||
|
"RecordFilingResult",
|
||||||
|
"RecordFilingService",
|
||||||
|
"RecordSourceAuthority",
|
||||||
|
"RecordSourceLocator",
|
||||||
|
"RecordSourceProvider",
|
||||||
|
"RecordSourceReference",
|
||||||
|
"record_source_capabilities",
|
||||||
|
"record_source_capability",
|
||||||
|
]
|
||||||
@@ -3612,7 +3612,7 @@ finally:
|
|||||||
"version_max_exclusive": "0.2.0",
|
"version_max_exclusive": "0.2.0",
|
||||||
}, modules["files"]["requires_interfaces"])
|
}, modules["files"]["requires_interfaces"])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
["campaigns", "encryption", "search"],
|
["campaigns", "encryption", "records", "search"],
|
||||||
modules["files"]["optional_dependencies"],
|
modules["files"]["optional_dependencies"],
|
||||||
)
|
)
|
||||||
self.assertIn({"name": "mail.campaign_delivery", "version": "0.2.0"}, modules["mail"]["provides_interfaces"])
|
self.assertIn({"name": "mail.campaign_delivery", "version": "0.2.0"}, modules["mail"]["provides_interfaces"])
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import UTC, datetime
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from govoplan_core.core.records import (
|
||||||
|
RecordContractError,
|
||||||
|
RecordFilingRequest,
|
||||||
|
RecordSourceLocator,
|
||||||
|
RecordSourceReference,
|
||||||
|
record_source_capabilities,
|
||||||
|
record_source_capability,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class _Registry:
|
||||||
|
def capability_names(self) -> tuple[str, ...]:
|
||||||
|
return ("mail.delivery", "records.source.cases", "records.source.files")
|
||||||
|
|
||||||
|
|
||||||
|
class RecordsContractTests(unittest.TestCase):
|
||||||
|
def test_exact_source_and_filing_request_are_tenant_bound(self) -> None:
|
||||||
|
locator = RecordSourceLocator(
|
||||||
|
tenant_id="tenant-1",
|
||||||
|
source_module="files",
|
||||||
|
resource_type="file_version",
|
||||||
|
resource_id="asset-1",
|
||||||
|
source_revision="version-7",
|
||||||
|
)
|
||||||
|
reference = RecordSourceReference(
|
||||||
|
locator=locator,
|
||||||
|
label="Evidence.pdf",
|
||||||
|
content_sha256="a" * 64,
|
||||||
|
size_bytes=12,
|
||||||
|
recorded_at=datetime(2026, 8, 6, tzinfo=UTC),
|
||||||
|
)
|
||||||
|
request = RecordFilingRequest(
|
||||||
|
tenant_id="tenant-1",
|
||||||
|
record_id="record-1",
|
||||||
|
source=locator,
|
||||||
|
purpose="process application",
|
||||||
|
filing_reason="Evidence received with the application.",
|
||||||
|
idempotency_key="filing-1",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual("version-7", reference.locator.source_revision)
|
||||||
|
self.assertEqual("record-1", request.record_id)
|
||||||
|
|
||||||
|
def test_cross_tenant_filing_and_inexact_sources_are_rejected(self) -> None:
|
||||||
|
with self.assertRaisesRegex(RecordContractError, "source_revision"):
|
||||||
|
RecordSourceLocator(
|
||||||
|
tenant_id="tenant-1",
|
||||||
|
source_module="files",
|
||||||
|
resource_type="file_version",
|
||||||
|
resource_id="asset-1",
|
||||||
|
source_revision="",
|
||||||
|
)
|
||||||
|
|
||||||
|
locator = RecordSourceLocator(
|
||||||
|
tenant_id="tenant-1",
|
||||||
|
source_module="cases",
|
||||||
|
resource_type="case_revision",
|
||||||
|
resource_id="case-1",
|
||||||
|
source_revision="4",
|
||||||
|
)
|
||||||
|
with self.assertRaisesRegex(RecordContractError, "cross tenants"):
|
||||||
|
RecordFilingRequest(
|
||||||
|
tenant_id="tenant-2",
|
||||||
|
record_id="record-1",
|
||||||
|
source=locator,
|
||||||
|
purpose="audit",
|
||||||
|
filing_reason="Preserve the decision basis.",
|
||||||
|
idempotency_key="filing-2",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_source_capabilities_are_discoverable_without_module_imports(self) -> None:
|
||||||
|
self.assertEqual("records.source.files", record_source_capability("files"))
|
||||||
|
self.assertEqual(
|
||||||
|
"records.source.forms_runtime",
|
||||||
|
record_source_capability("forms_runtime"),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
("records.source.cases", "records.source.files"),
|
||||||
|
record_source_capabilities(_Registry()),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Generated
+21
@@ -36,6 +36,7 @@
|
|||||||
"@govoplan/portal-webui": "file:../../govoplan-portal/webui",
|
"@govoplan/portal-webui": "file:../../govoplan-portal/webui",
|
||||||
"@govoplan/postbox-webui": "file:../../govoplan-postbox/webui",
|
"@govoplan/postbox-webui": "file:../../govoplan-postbox/webui",
|
||||||
"@govoplan/projects-webui": "file:../../govoplan-projects/webui",
|
"@govoplan/projects-webui": "file:../../govoplan-projects/webui",
|
||||||
|
"@govoplan/records-webui": "file:../../govoplan-records/webui",
|
||||||
"@govoplan/reporting-webui": "file:../../govoplan-reporting/webui",
|
"@govoplan/reporting-webui": "file:../../govoplan-reporting/webui",
|
||||||
"@govoplan/risk-compliance-webui": "file:../../govoplan-risk-compliance/webui",
|
"@govoplan/risk-compliance-webui": "file:../../govoplan-risk-compliance/webui",
|
||||||
"@govoplan/scheduling-webui": "file:../../govoplan-scheduling/webui",
|
"@govoplan/scheduling-webui": "file:../../govoplan-scheduling/webui",
|
||||||
@@ -555,6 +556,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"../../govoplan-records/webui": {
|
||||||
|
"name": "@govoplan/records-webui",
|
||||||
|
"version": "0.1.18",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@govoplan/core-webui": "^0.1.18",
|
||||||
|
"lucide-react": "^1.23.0",
|
||||||
|
"react": ">=19.2.7 <20",
|
||||||
|
"react-dom": ">=19.2.7 <20",
|
||||||
|
"react-router": ">=8.3.0 <9"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@govoplan/core-webui": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"../../govoplan-reporting/webui": {
|
"../../govoplan-reporting/webui": {
|
||||||
"name": "@govoplan/reporting-webui",
|
"name": "@govoplan/reporting-webui",
|
||||||
"version": "0.1.18",
|
"version": "0.1.18",
|
||||||
@@ -1567,6 +1584,10 @@
|
|||||||
"resolved": "../../govoplan-projects/webui",
|
"resolved": "../../govoplan-projects/webui",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@govoplan/records-webui": {
|
||||||
|
"resolved": "../../govoplan-records/webui",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@govoplan/reporting-webui": {
|
"node_modules/@govoplan/reporting-webui": {
|
||||||
"resolved": "../../govoplan-reporting/webui",
|
"resolved": "../../govoplan-reporting/webui",
|
||||||
"link": true
|
"link": true
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"@govoplan/postbox-webui": "file:../../govoplan-postbox/webui",
|
"@govoplan/postbox-webui": "file:../../govoplan-postbox/webui",
|
||||||
"@govoplan/projects-webui": "file:../../govoplan-projects/webui",
|
"@govoplan/projects-webui": "file:../../govoplan-projects/webui",
|
||||||
"@govoplan/reporting-webui": "file:../../govoplan-reporting/webui",
|
"@govoplan/reporting-webui": "file:../../govoplan-reporting/webui",
|
||||||
|
"@govoplan/records-webui": "file:../../govoplan-records/webui",
|
||||||
"@govoplan/risk-compliance-webui": "file:../../govoplan-risk-compliance/webui",
|
"@govoplan/risk-compliance-webui": "file:../../govoplan-risk-compliance/webui",
|
||||||
"@govoplan/scheduling-webui": "file:../../govoplan-scheduling/webui",
|
"@govoplan/scheduling-webui": "file:../../govoplan-scheduling/webui",
|
||||||
"@govoplan/search-webui": "file:../../govoplan-search/webui",
|
"@govoplan/search-webui": "file:../../govoplan-search/webui",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const packageByModule = {
|
|||||||
postbox: "@govoplan/postbox-webui",
|
postbox: "@govoplan/postbox-webui",
|
||||||
projects: "@govoplan/projects-webui",
|
projects: "@govoplan/projects-webui",
|
||||||
reporting: "@govoplan/reporting-webui",
|
reporting: "@govoplan/reporting-webui",
|
||||||
|
records: "@govoplan/records-webui",
|
||||||
risk_compliance: "@govoplan/risk-compliance-webui",
|
risk_compliance: "@govoplan/risk-compliance-webui",
|
||||||
scheduling: "@govoplan/scheduling-webui",
|
scheduling: "@govoplan/scheduling-webui",
|
||||||
search: "@govoplan/search-webui",
|
search: "@govoplan/search-webui",
|
||||||
@@ -80,6 +81,8 @@ const cases = [
|
|||||||
{ name: "portal-only", modules: ["portal"] },
|
{ name: "portal-only", modules: ["portal"] },
|
||||||
{ name: "projects-only", modules: ["projects"] },
|
{ name: "projects-only", modules: ["projects"] },
|
||||||
{ name: "reporting-only", modules: ["reporting"] },
|
{ name: "reporting-only", modules: ["reporting"] },
|
||||||
|
{ name: "records-only", modules: ["access", "records"] },
|
||||||
|
{ name: "records-with-sources", modules: ["access", "cases", "files", "records"] },
|
||||||
{ name: "campaign-only", modules: ["campaigns"] },
|
{ name: "campaign-only", modules: ["campaigns"] },
|
||||||
{ name: "campaign-with-postbox", modules: ["campaigns", "postbox"] },
|
{ name: "campaign-with-postbox", modules: ["campaigns", "postbox"] },
|
||||||
{ name: "campaign-with-files-no-mail", modules: ["campaigns", "files"] },
|
{ name: "campaign-with-files-no-mail", modules: ["campaigns", "files"] },
|
||||||
@@ -91,7 +94,7 @@ const cases = [
|
|||||||
{ name: "docs-and-ops", modules: ["access", "docs", "ops"] },
|
{ name: "docs-and-ops", modules: ["access", "docs", "ops"] },
|
||||||
{ name: "approvals-only", modules: ["access", "approvals"] },
|
{ name: "approvals-only", modules: ["access", "approvals"] },
|
||||||
{ name: "voting-only", modules: ["access", "voting"] },
|
{ name: "voting-only", modules: ["access", "voting"] },
|
||||||
{ name: "full-product", modules: ["access", "tenancy", "admin", "addresses", "approvals", "policy", "audit", "dashboard", "datasources", "dataflow", "dist_lists", "templates", "workflow", "views", "organizations", "idm", "identity_trust", "encryption", "cases", "committee", "campaigns", "files", "forms", "forms_runtime", "mail", "notifications", "docs", "ops", "calendar", "scheduling", "portal", "postbox", "projects", "reporting", "risk_compliance", "search", "voting"] }
|
{ name: "full-product", modules: ["access", "tenancy", "admin", "addresses", "approvals", "policy", "audit", "dashboard", "datasources", "dataflow", "dist_lists", "templates", "workflow", "views", "organizations", "idm", "identity_trust", "encryption", "cases", "committee", "campaigns", "files", "forms", "forms_runtime", "mail", "notifications", "docs", "ops", "calendar", "scheduling", "portal", "postbox", "projects", "reporting", "records", "risk_compliance", "search", "voting"] }
|
||||||
];
|
];
|
||||||
|
|
||||||
const npmExec = process.env.npm_execpath;
|
const npmExec = process.env.npm_execpath;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const defaultWebModulePackages = [
|
|||||||
"@govoplan/postbox-webui",
|
"@govoplan/postbox-webui",
|
||||||
"@govoplan/projects-webui",
|
"@govoplan/projects-webui",
|
||||||
"@govoplan/reporting-webui",
|
"@govoplan/reporting-webui",
|
||||||
|
"@govoplan/records-webui",
|
||||||
"@govoplan/risk-compliance-webui",
|
"@govoplan/risk-compliance-webui",
|
||||||
"@govoplan/scheduling-webui",
|
"@govoplan/scheduling-webui",
|
||||||
"@govoplan/search-webui",
|
"@govoplan/search-webui",
|
||||||
|
|||||||
Reference in New Issue
Block a user