financial-data-protocol 0.3.0a3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- financial_data_protocol/__init__.py +187 -0
- financial_data_protocol/__main__.py +3 -0
- financial_data_protocol/cli/__init__.py +186 -0
- financial_data_protocol/cli/__main__.py +3 -0
- financial_data_protocol/client/__init__.py +25 -0
- financial_data_protocol/client/client.py +349 -0
- financial_data_protocol/client/httpclient.py +478 -0
- financial_data_protocol/client/interfaces.py +149 -0
- financial_data_protocol/client/protocol_client.py +320 -0
- financial_data_protocol/client/test_client.py +201 -0
- financial_data_protocol/constants.py +5 -0
- financial_data_protocol/models.py +352 -0
- financial_data_protocol/protocol.py +64 -0
- financial_data_protocol/py.typed +1 -0
- financial_data_protocol/server/__init__.py +12 -0
- financial_data_protocol/server/api.py +707 -0
- financial_data_protocol/server/app.py +85 -0
- financial_data_protocol/server/authority.py +322 -0
- financial_data_protocol/server/config.py +162 -0
- financial_data_protocol/server/db/__init__.py +1 -0
- financial_data_protocol/server/db/mongo/__init__.py +5 -0
- financial_data_protocol/server/db/mongo/schema.py +384 -0
- financial_data_protocol/server/db/postgres/__init__.py +1 -0
- financial_data_protocol/server/db/postgres/migrations/001_initial.sql +164 -0
- financial_data_protocol/server/db/postgres/migrations/002_lineage_receipts.sql +9 -0
- financial_data_protocol/server/db/postgres/migrations/003_material_handoffs.sql +43 -0
- financial_data_protocol/server/db/postgres/migrations/004_disclosures.sql +9 -0
- financial_data_protocol/server/db/postgres/migrations/005_domain_authority.sql +9 -0
- financial_data_protocol/server/db/postgres/migrations/006_content_blobs.sql +16 -0
- financial_data_protocol/server/db/postgres/migrations/__init__.py +1 -0
- financial_data_protocol/server/errors.py +51 -0
- financial_data_protocol/server/http.py +857 -0
- financial_data_protocol/server/logging.py +61 -0
- financial_data_protocol/server/object_storage/__init__.py +16 -0
- financial_data_protocol/server/object_storage/content_store.py +258 -0
- financial_data_protocol/server/object_storage/in_memory_content.py +44 -0
- financial_data_protocol/server/object_storage/interfaces.py +28 -0
- financial_data_protocol/server/registries/__init__.py +39 -0
- financial_data_protocol/server/registries/in_memory_registry.py +768 -0
- financial_data_protocol/server/registries/interfaces.py +295 -0
- financial_data_protocol/server/registries/mongo_registry.py +1642 -0
- financial_data_protocol/server/registries/postgres_registry.py +1875 -0
- financial_data_protocol/server/schema.py +73 -0
- financial_data_protocol/server/services/__init__.py +37 -0
- financial_data_protocol/server/services/artifact_service.py +105 -0
- financial_data_protocol/server/services/central_service.py +751 -0
- financial_data_protocol/server/services/content_service.py +549 -0
- financial_data_protocol-0.3.0a3.dist-info/METADATA +203 -0
- financial_data_protocol-0.3.0a3.dist-info/RECORD +52 -0
- financial_data_protocol-0.3.0a3.dist-info/WHEEL +5 -0
- financial_data_protocol-0.3.0a3.dist-info/entry_points.txt +2 -0
- financial_data_protocol-0.3.0a3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Financial Data Protocol public package."""
|
|
2
|
+
|
|
3
|
+
from .client import FdpClient, FdpConfig
|
|
4
|
+
from .client.protocol_client import CentralClient, CentralClientError
|
|
5
|
+
from .constants import API_SCHEMA_VERSION, CENTRAL_SCHEMA_REVISION, CENTRAL_SCHEMA_VERSION
|
|
6
|
+
from .models import (
|
|
7
|
+
Artifact,
|
|
8
|
+
Content,
|
|
9
|
+
DeclarationProvenance,
|
|
10
|
+
DisclosurePackageRevision,
|
|
11
|
+
DisclosureReceipt,
|
|
12
|
+
DisclosureRevocation,
|
|
13
|
+
EvidenceReference,
|
|
14
|
+
LineageDeclaration,
|
|
15
|
+
LineageRelationType,
|
|
16
|
+
MaterialTransfer,
|
|
17
|
+
Package,
|
|
18
|
+
PackageMember,
|
|
19
|
+
PackageRevision,
|
|
20
|
+
RegistrySpace,
|
|
21
|
+
SpaceAccess,
|
|
22
|
+
UsageReceipt,
|
|
23
|
+
WriteConflict,
|
|
24
|
+
WriteCreated,
|
|
25
|
+
WriteIdempotent,
|
|
26
|
+
WriteResult,
|
|
27
|
+
)
|
|
28
|
+
from .server.api import (
|
|
29
|
+
ApiFailure,
|
|
30
|
+
ApiRequest,
|
|
31
|
+
ApiResponse,
|
|
32
|
+
ApiSuccess,
|
|
33
|
+
CentralTransport,
|
|
34
|
+
InProcessCentralTransport,
|
|
35
|
+
StaticMachineTokenAuthenticator,
|
|
36
|
+
StaticMachineTokenBinding,
|
|
37
|
+
)
|
|
38
|
+
from .server.authority import (
|
|
39
|
+
AuthorityCutoverRecord,
|
|
40
|
+
AuthorityRollbackPointRecord,
|
|
41
|
+
AuthorityRollbackRecord,
|
|
42
|
+
DomainAuthorityGate,
|
|
43
|
+
DomainAuthorityReadback,
|
|
44
|
+
DomainAuthorityState,
|
|
45
|
+
ShadowComparison,
|
|
46
|
+
)
|
|
47
|
+
from .server.config import CentralSettings
|
|
48
|
+
from .server.db.mongo.schema import CENTRAL_MONGO_SCHEMA_REVISION
|
|
49
|
+
from .server.errors import (
|
|
50
|
+
AuthorizationError,
|
|
51
|
+
ContentIntegrityError,
|
|
52
|
+
NotFoundError,
|
|
53
|
+
OperationStateError,
|
|
54
|
+
ServiceError,
|
|
55
|
+
)
|
|
56
|
+
from .server.http import CentralHttpApp, HttpCentralTransport, create_app
|
|
57
|
+
from .server.object_storage import (
|
|
58
|
+
AlibabaCloudOssObjectStore,
|
|
59
|
+
InMemoryObjectStore,
|
|
60
|
+
ObjectStore,
|
|
61
|
+
S3ObjectStore,
|
|
62
|
+
StreamingObjectStore,
|
|
63
|
+
)
|
|
64
|
+
from .server.registries import (
|
|
65
|
+
CentralOperationState,
|
|
66
|
+
CleanupUploadRequest,
|
|
67
|
+
FinalizedUpload,
|
|
68
|
+
FinalizeUploadRequest,
|
|
69
|
+
IdempotencyRecord,
|
|
70
|
+
InMemoryRegistryCustody,
|
|
71
|
+
RegistryCustody,
|
|
72
|
+
StagedUpload,
|
|
73
|
+
StageUploadRequest,
|
|
74
|
+
)
|
|
75
|
+
from .server.registries.mongo_registry import CentralMongoRegistry
|
|
76
|
+
from .server.registries.postgres_registry import (
|
|
77
|
+
CENTRAL_POSTGRES_SCHEMA_REVISION,
|
|
78
|
+
CentralPostgresRegistry,
|
|
79
|
+
)
|
|
80
|
+
from .server.schema import export_all_json_schemas, export_json_schema, schema_names
|
|
81
|
+
from .server.services import (
|
|
82
|
+
ArtifactService,
|
|
83
|
+
BackupRestoreReadiness,
|
|
84
|
+
CentralAccessContext,
|
|
85
|
+
CentralService,
|
|
86
|
+
ContextSpaceAuthorizer,
|
|
87
|
+
RecoveryContentCheck,
|
|
88
|
+
RecoveryRecordCheck,
|
|
89
|
+
RecoveryRecordState,
|
|
90
|
+
RecoveryReport,
|
|
91
|
+
SpaceAuthorizer,
|
|
92
|
+
)
|
|
93
|
+
from .server.services.content_service import (
|
|
94
|
+
ContentVerificationState,
|
|
95
|
+
FinalizedContentInspection,
|
|
96
|
+
OrphanedObject,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
__all__ = [
|
|
100
|
+
"API_SCHEMA_VERSION",
|
|
101
|
+
"CENTRAL_MONGO_SCHEMA_REVISION",
|
|
102
|
+
"CENTRAL_POSTGRES_SCHEMA_REVISION",
|
|
103
|
+
"CENTRAL_SCHEMA_REVISION",
|
|
104
|
+
"CENTRAL_SCHEMA_VERSION",
|
|
105
|
+
"AlibabaCloudOssObjectStore",
|
|
106
|
+
"ApiFailure",
|
|
107
|
+
"ApiRequest",
|
|
108
|
+
"ApiResponse",
|
|
109
|
+
"ApiSuccess",
|
|
110
|
+
"Artifact",
|
|
111
|
+
"ArtifactService",
|
|
112
|
+
"AuthorityCutoverRecord",
|
|
113
|
+
"AuthorityRollbackPointRecord",
|
|
114
|
+
"AuthorityRollbackRecord",
|
|
115
|
+
"AuthorizationError",
|
|
116
|
+
"BackupRestoreReadiness",
|
|
117
|
+
"CentralAccessContext",
|
|
118
|
+
"CentralClient",
|
|
119
|
+
"CentralClientError",
|
|
120
|
+
"CentralHttpApp",
|
|
121
|
+
"CentralMongoRegistry",
|
|
122
|
+
"CentralOperationState",
|
|
123
|
+
"CentralPostgresRegistry",
|
|
124
|
+
"CentralService",
|
|
125
|
+
"CentralSettings",
|
|
126
|
+
"CentralTransport",
|
|
127
|
+
"CleanupUploadRequest",
|
|
128
|
+
"Content",
|
|
129
|
+
"ContentIntegrityError",
|
|
130
|
+
"ContentVerificationState",
|
|
131
|
+
"ContextSpaceAuthorizer",
|
|
132
|
+
"DeclarationProvenance",
|
|
133
|
+
"DisclosurePackageRevision",
|
|
134
|
+
"DisclosureReceipt",
|
|
135
|
+
"DisclosureRevocation",
|
|
136
|
+
"DomainAuthorityGate",
|
|
137
|
+
"DomainAuthorityReadback",
|
|
138
|
+
"DomainAuthorityState",
|
|
139
|
+
"EvidenceReference",
|
|
140
|
+
"FdpClient",
|
|
141
|
+
"FdpConfig",
|
|
142
|
+
"FinalizeUploadRequest",
|
|
143
|
+
"FinalizedContentInspection",
|
|
144
|
+
"FinalizedUpload",
|
|
145
|
+
"HttpCentralTransport",
|
|
146
|
+
"IdempotencyRecord",
|
|
147
|
+
"InMemoryObjectStore",
|
|
148
|
+
"InMemoryRegistryCustody",
|
|
149
|
+
"InProcessCentralTransport",
|
|
150
|
+
"LineageDeclaration",
|
|
151
|
+
"LineageRelationType",
|
|
152
|
+
"MaterialTransfer",
|
|
153
|
+
"NotFoundError",
|
|
154
|
+
"ObjectStore",
|
|
155
|
+
"OperationStateError",
|
|
156
|
+
"OrphanedObject",
|
|
157
|
+
"Package",
|
|
158
|
+
"PackageMember",
|
|
159
|
+
"PackageRevision",
|
|
160
|
+
"RecoveryContentCheck",
|
|
161
|
+
"RecoveryRecordCheck",
|
|
162
|
+
"RecoveryRecordState",
|
|
163
|
+
"RecoveryReport",
|
|
164
|
+
"RegistryCustody",
|
|
165
|
+
"RegistrySpace",
|
|
166
|
+
"S3ObjectStore",
|
|
167
|
+
"ServiceError",
|
|
168
|
+
"ShadowComparison",
|
|
169
|
+
"SpaceAccess",
|
|
170
|
+
"SpaceAuthorizer",
|
|
171
|
+
"StageUploadRequest",
|
|
172
|
+
"StagedUpload",
|
|
173
|
+
"StaticMachineTokenAuthenticator",
|
|
174
|
+
"StaticMachineTokenBinding",
|
|
175
|
+
"StreamingObjectStore",
|
|
176
|
+
"UsageReceipt",
|
|
177
|
+
"WriteConflict",
|
|
178
|
+
"WriteCreated",
|
|
179
|
+
"WriteIdempotent",
|
|
180
|
+
"WriteResult",
|
|
181
|
+
"create_app",
|
|
182
|
+
"export_all_json_schemas",
|
|
183
|
+
"export_json_schema",
|
|
184
|
+
"schema_names",
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
__version__ = "0.3.0a2"
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Explicit, configuration-authenticated Central custody reconciliation CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
from typing import Any, Never
|
|
10
|
+
from urllib.error import HTTPError, URLError
|
|
11
|
+
from urllib.parse import urlencode
|
|
12
|
+
from urllib.request import Request, urlopen
|
|
13
|
+
|
|
14
|
+
from ..server.app import load_server_settings
|
|
15
|
+
from ..server.config import OperatorSettings
|
|
16
|
+
from ..server.errors import FDPError
|
|
17
|
+
from ..server.http import HttpCentralTransport
|
|
18
|
+
from ..server.registries.mongo_registry import CentralMongoRegistry
|
|
19
|
+
|
|
20
|
+
EXIT_OK = 0
|
|
21
|
+
EXIT_USAGE = 2
|
|
22
|
+
EXIT_OPERATIONAL_ERROR = 4
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _emit(payload: Any, *, stream: Any = sys.stdout) -> None:
|
|
26
|
+
print(json.dumps(payload, ensure_ascii=False, sort_keys=True), file=stream)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _Parser(argparse.ArgumentParser):
|
|
30
|
+
def error(self, message: str) -> Never:
|
|
31
|
+
_emit({"ok": False, "error": "invalid_arguments", "message": message}, stream=sys.stderr)
|
|
32
|
+
raise SystemExit(EXIT_USAGE)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _parser() -> _Parser:
|
|
36
|
+
parser = _Parser(prog="fdp-central", description="FDP Central operator commands")
|
|
37
|
+
commands = parser.add_subparsers(dest="command", required=True)
|
|
38
|
+
sweep = commands.add_parser(
|
|
39
|
+
"blob-sweep",
|
|
40
|
+
help="reconcile final Blob objects for a month, for example --month 2026-08",
|
|
41
|
+
)
|
|
42
|
+
sweep.add_argument("--registry-space-id", required=True)
|
|
43
|
+
sweep.add_argument("--caller-id", required=True)
|
|
44
|
+
sweep.add_argument(
|
|
45
|
+
"--month",
|
|
46
|
+
required=True,
|
|
47
|
+
help="UTC Blob creation month in YYYY-MM format, for example 2026-08",
|
|
48
|
+
)
|
|
49
|
+
sweep.add_argument("--max-keys", type=int, default=1000)
|
|
50
|
+
sweep.add_argument("--apply", action="store_true")
|
|
51
|
+
commands.add_parser(
|
|
52
|
+
"init-db", help="initialize the configured Central MongoDB registry (empty database only)"
|
|
53
|
+
)
|
|
54
|
+
commands.add_parser("serve", help="run the configured Central HTTP service")
|
|
55
|
+
return parser
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _transport(caller_id: str) -> HttpCentralTransport:
|
|
59
|
+
"""Read operator transport settings from the shared YAML configuration."""
|
|
60
|
+
configured = OperatorSettings.from_file()
|
|
61
|
+
if caller_id != configured.caller_id:
|
|
62
|
+
raise ValueError("Caller ID does not match the configured operator identity")
|
|
63
|
+
return HttpCentralTransport(configured.http_url, configured.machine_token, caller_id)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _send(
|
|
67
|
+
transport: HttpCentralTransport,
|
|
68
|
+
*,
|
|
69
|
+
caller_id: str,
|
|
70
|
+
space: str,
|
|
71
|
+
operation: str,
|
|
72
|
+
payload: dict[str, Any],
|
|
73
|
+
key: str | None = None,
|
|
74
|
+
) -> dict[str, Any]:
|
|
75
|
+
if operation == "list_orphaned_objects":
|
|
76
|
+
url = transport.base_url + "/v1/content/orphaned-objects?" + urlencode(payload)
|
|
77
|
+
method, body = "GET", None
|
|
78
|
+
elif operation == "remove_orphaned_object":
|
|
79
|
+
url = transport.base_url + "/v1/content/orphaned-objects:remove"
|
|
80
|
+
method, body = "POST", json.dumps(payload).encode()
|
|
81
|
+
elif operation == "cleanup_upload":
|
|
82
|
+
operation_id = str(payload["operation_id"])
|
|
83
|
+
url = transport.base_url + f"/v1/content/stages/{operation_id}:cleanup"
|
|
84
|
+
method, body = "POST", b""
|
|
85
|
+
else: # pragma: no cover - closed command surface
|
|
86
|
+
raise ValueError("unsupported custody-sweep operation")
|
|
87
|
+
headers = {
|
|
88
|
+
"Authorization": "Bearer " + transport.machine_token,
|
|
89
|
+
"FDP-Caller-Id": caller_id,
|
|
90
|
+
"FDP-Registry-Space-Id": space,
|
|
91
|
+
}
|
|
92
|
+
if body is not None:
|
|
93
|
+
headers["Content-Type"] = "application/json"
|
|
94
|
+
if key is not None:
|
|
95
|
+
headers["Idempotency-Key"] = key
|
|
96
|
+
try:
|
|
97
|
+
with urlopen(
|
|
98
|
+
Request(url, data=body, headers=headers, method=method), timeout=transport.timeout
|
|
99
|
+
) as raw:
|
|
100
|
+
response = json.loads(raw.read())
|
|
101
|
+
except HTTPError as exc:
|
|
102
|
+
response = json.loads(exc.read())
|
|
103
|
+
except URLError as exc:
|
|
104
|
+
raise RuntimeError("Central HTTP transport is unavailable") from exc
|
|
105
|
+
if response.get("result") != "success":
|
|
106
|
+
raise RuntimeError(str(response.get("code", "unknown_error")))
|
|
107
|
+
return dict(response["data"])
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
111
|
+
args = _parser().parse_args(argv)
|
|
112
|
+
if args.command == "serve":
|
|
113
|
+
try:
|
|
114
|
+
from ..server.app import main as start_server
|
|
115
|
+
|
|
116
|
+
return start_server()
|
|
117
|
+
except (FDPError, OSError, RuntimeError, ValueError) as exc:
|
|
118
|
+
_emit(
|
|
119
|
+
{
|
|
120
|
+
"ok": False,
|
|
121
|
+
"error": getattr(exc, "code", "operational_error"),
|
|
122
|
+
"message": str(exc),
|
|
123
|
+
},
|
|
124
|
+
stream=sys.stderr,
|
|
125
|
+
)
|
|
126
|
+
return EXIT_OPERATIONAL_ERROR
|
|
127
|
+
if args.command == "init-db":
|
|
128
|
+
try:
|
|
129
|
+
settings = load_server_settings()
|
|
130
|
+
registry = CentralMongoRegistry(settings.database_url or "")
|
|
131
|
+
try:
|
|
132
|
+
_emit({"ok": True, "central_schema_revision": registry.migrate()})
|
|
133
|
+
finally:
|
|
134
|
+
registry.close()
|
|
135
|
+
return EXIT_OK
|
|
136
|
+
except (FDPError, OSError, RuntimeError, ValueError) as exc:
|
|
137
|
+
_emit(
|
|
138
|
+
{"ok": False, "error": "operational_error", "message": str(exc)},
|
|
139
|
+
stream=sys.stderr,
|
|
140
|
+
)
|
|
141
|
+
return EXIT_OPERATIONAL_ERROR
|
|
142
|
+
if not 1 <= args.max_keys <= 1000:
|
|
143
|
+
_emit(
|
|
144
|
+
{"ok": False, "error": "invalid_arguments", "message": "--max-keys is 1..1000"},
|
|
145
|
+
stream=sys.stderr,
|
|
146
|
+
)
|
|
147
|
+
return EXIT_USAGE
|
|
148
|
+
try:
|
|
149
|
+
transport = _transport(args.caller_id)
|
|
150
|
+
listed = _send(
|
|
151
|
+
transport,
|
|
152
|
+
caller_id=args.caller_id,
|
|
153
|
+
space=args.registry_space_id,
|
|
154
|
+
operation="list_orphaned_objects",
|
|
155
|
+
payload={"month": args.month, "max_keys": args.max_keys},
|
|
156
|
+
)
|
|
157
|
+
objects = [str(item["object_key"]) for item in listed["objects"]]
|
|
158
|
+
report: dict[str, Any] = {
|
|
159
|
+
"ok": True,
|
|
160
|
+
"mode": "apply" if args.apply else "dry_run",
|
|
161
|
+
"orphaned_objects": objects,
|
|
162
|
+
}
|
|
163
|
+
if args.apply:
|
|
164
|
+
removals: list[dict[str, Any]] = []
|
|
165
|
+
for index, object_key in enumerate(objects):
|
|
166
|
+
key = f"custody-sweep-remove-{index}-{object_key}"
|
|
167
|
+
result = _send(
|
|
168
|
+
transport,
|
|
169
|
+
caller_id=args.caller_id,
|
|
170
|
+
space=args.registry_space_id,
|
|
171
|
+
operation="remove_orphaned_object",
|
|
172
|
+
payload={"object_key": object_key},
|
|
173
|
+
key=key,
|
|
174
|
+
)
|
|
175
|
+
removals.append(
|
|
176
|
+
{
|
|
177
|
+
"object_key": object_key,
|
|
178
|
+
"removed": result["removed"],
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
report["removals"] = removals
|
|
182
|
+
_emit(report)
|
|
183
|
+
return EXIT_OK
|
|
184
|
+
except (OSError, RuntimeError, ValueError) as exc:
|
|
185
|
+
_emit({"ok": False, "error": "operational_error", "message": str(exc)}, stream=sys.stderr)
|
|
186
|
+
return EXIT_OPERATIONAL_ERROR
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Reusable clients for Financial Data Protocol."""
|
|
2
|
+
|
|
3
|
+
from .client import (
|
|
4
|
+
DEFAULT_FDP_CONFIG,
|
|
5
|
+
ArtifactUpload,
|
|
6
|
+
FdpClient,
|
|
7
|
+
PackageRevisionDraft,
|
|
8
|
+
PackageUploadResult,
|
|
9
|
+
)
|
|
10
|
+
from .httpclient import FdpConfig, HttpClient
|
|
11
|
+
from .interfaces import ArtifactPackageClient
|
|
12
|
+
from .protocol_client import CentralClient, CentralClientError
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"DEFAULT_FDP_CONFIG",
|
|
16
|
+
"ArtifactPackageClient",
|
|
17
|
+
"ArtifactUpload",
|
|
18
|
+
"CentralClient",
|
|
19
|
+
"CentralClientError",
|
|
20
|
+
"FdpClient",
|
|
21
|
+
"FdpConfig",
|
|
22
|
+
"HttpClient",
|
|
23
|
+
"PackageRevisionDraft",
|
|
24
|
+
"PackageUploadResult",
|
|
25
|
+
]
|