matelab-python-sdk 0.2.0a4__tar.gz → 0.2.0a5__tar.gz
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.
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/CONTEXT.md +6 -1
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/PKG-INFO +1 -1
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/contracts/matelab-integration-v1.lock.json +3 -3
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/contracts/matelab-integration-v1.openapi.yaml +95 -39
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/docs/api.md +25 -6
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/docs/operation-coverage.yaml +2 -2
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/pyproject.toml +1 -1
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/__init__.py +2 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/_generated/models.py +16 -1
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/records.py +51 -10
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_records.py +105 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/typing/consumer.py +8 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/uv.lock +1 -1
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/.env.example +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/.github/workflows/release.yml +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/.gitignore +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/AGENTS.md +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/LICENSE +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/NOTICE +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/README.md +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/docs/agent-architecture.md +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/docs/agent-workflows.md +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/docs/development.md +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/scripts/check_installed_package.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/scripts/check_release.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/scripts/generate_models.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/_generated/__init__.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/_hashes.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/_transport.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/_wire_validation.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/client.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/__init__.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/_attachment_content.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/cloud_drive.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/groups.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/literature.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/notebooks.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/templates.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/uploads.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/users.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/errors.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/py.typed +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/session.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/streaming.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/conftest.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/provider/test_provider_smoke.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_async_attachment_upload.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_client.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_cloud_drive.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_comments_and_downloads.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_consumer_smoke.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_errors.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_extended_records.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_generation.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_groups.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_notebook_lifecycle.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_notebooks.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_operation_coverage.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_record_lifecycle.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_record_mutations.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_release.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_streaming.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_template_lifecycle.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_templates.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_transport.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_uploads_and_literature.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_users.py +0 -0
- {matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/typing/pyproject.toml +0 -0
|
@@ -63,9 +63,14 @@ _Avoid_: Record locator, public record ref, deleted record ref
|
|
|
63
63
|
|
|
64
64
|
**Record locator**:
|
|
65
65
|
A record ref paired with the owned or shared notebook in which it was observed. It is the complete source identity
|
|
66
|
-
used by record reads and record-scoped operations.
|
|
66
|
+
used by owned/shared record reads and record-scoped operations.
|
|
67
67
|
_Avoid_: Record ref, notebook ID
|
|
68
68
|
|
|
69
|
+
**Public record ref**:
|
|
70
|
+
A public association's row ID, paired source notebook and record IDs, and nullable association UID metadata.
|
|
71
|
+
The source ID pair locates its current body; it does not grant notebook-wide access.
|
|
72
|
+
_Avoid_: Record locator, public notebook ID, association ID as record ID
|
|
73
|
+
|
|
69
74
|
**Record version ref**:
|
|
70
75
|
A historical version ID bound to its record locator. It identifies observed historical content, not a separate active
|
|
71
76
|
record.
|
{matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/contracts/matelab-integration-v1.lock.json
RENAMED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"contract_line": "integration-v1",
|
|
4
4
|
"release": {
|
|
5
|
-
"tag": "v2026.10.
|
|
6
|
-
"commit": "
|
|
5
|
+
"tag": "v2026.10.2",
|
|
6
|
+
"commit": "1183d6833d88a6904598357321ef2d30cf944339"
|
|
7
7
|
},
|
|
8
8
|
"openapi": {
|
|
9
9
|
"source_path": "openapi/matelab-integration-v1.openapi.yaml",
|
|
10
10
|
"snapshot": "matelab-integration-v1.openapi.yaml",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "4806fc2e4b4cb4cf94464be6f1b5f574f5a44745bf7cbc03d8644a13e57f8ec9"
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: Matelab Integration Contract
|
|
4
|
-
version: "2026.10.
|
|
4
|
+
version: "2026.10.2"
|
|
5
5
|
description: |
|
|
6
6
|
Current PHP wire contract for generating SDKs against the selected
|
|
7
7
|
`matelab-server` Provider version.
|
|
@@ -26,6 +26,7 @@ info:
|
|
|
26
26
|
- controller/Eln.php
|
|
27
27
|
- controller/Eln_api.php
|
|
28
28
|
- controller/Eln_items.php
|
|
29
|
+
- controller/Eln_public.php
|
|
29
30
|
- controller/Eln_template.php
|
|
30
31
|
- controller/Eln_url.php
|
|
31
32
|
- controller/Files.php
|
|
@@ -36,6 +37,7 @@ info:
|
|
|
36
37
|
- model/FileQuoteModel.php
|
|
37
38
|
- model/FilesModel.php
|
|
38
39
|
- traits/ElnTrait.php
|
|
40
|
+
- traits/ElnPublicTrait.php
|
|
39
41
|
- traits/RecommendTrait.php
|
|
40
42
|
- service/ElnApiImport.php
|
|
41
43
|
- service/ElnApiUpdate.php
|
|
@@ -53,7 +55,7 @@ info:
|
|
|
53
55
|
- sql/recommend.sql
|
|
54
56
|
x-provider-audit:
|
|
55
57
|
currentCommit: 3f39d47b7ad43bed618b029a7d9cc51fab852eb6
|
|
56
|
-
previousReleaseEvidenceCommit:
|
|
58
|
+
previousReleaseEvidenceCommit: 3f39d47b7ad43bed618b029a7d9cc51fab852eb6
|
|
57
59
|
lastProviderVerifiedCommit: 3f39d47b7ad43bed618b029a7d9cc51fab852eb6
|
|
58
60
|
contractStatus: current-provider-aligned
|
|
59
61
|
# Summary of changes adopted by current Supported Routes since the last
|
|
@@ -62,36 +64,8 @@ info:
|
|
|
62
64
|
# updates against that fixed release baseline; use [] if no relevant changes.
|
|
63
65
|
# Entries are not per-change verification results or a cumulative history.
|
|
64
66
|
incorporatedChanges:
|
|
65
|
-
- {operationId:
|
|
66
|
-
- {operationId:
|
|
67
|
-
- {operationId: readRecord, change: encrypted-modules-and-unresolved-user-fields-modeled}
|
|
68
|
-
- {operationId: listRecordComments, change: unresolved-author-descriptors-modeled}
|
|
69
|
-
- {operationId: listRecordRelations, change: unresolved-comment-author-descriptors-modeled}
|
|
70
|
-
- {operationId: updateRecordData, change: staged-files-folder-append-wired-into-request-and-base64-excluded}
|
|
71
|
-
- {operationId: importRecordDataset, change: unreachable-base64-file-input-excluded}
|
|
72
|
-
- {operationId: searchRecordData, change: fixed-result-field-aliases-reserved-and-extraction-path-constrained}
|
|
73
|
-
- {operationId: saveNotebookMetadata, change: decoded-list-display-requests-reject-null-uid-and-invalid-path}
|
|
74
|
-
- {operationId: updateRecordData, change: quote-extraction-path-constrained-and-collaboration-code-0-documented}
|
|
75
|
-
- {operationId: listNotebooks, change: stored-types-return-non-null-decoded-json}
|
|
76
|
-
- {operationId: extractLiteratureMetadata, change: nested-errors-and-opaque-array-results-modeled}
|
|
77
|
-
- {operationId: listCloudDriveFiles, change: unreachable-null-timestamp-success-removed}
|
|
78
|
-
- {operationId: downloadRecordAttachment, change: zero-start-open-range-4097-byte-cap-documented}
|
|
79
|
-
- {operationId: downloadRecordCommentAttachment, change: zero-start-open-range-4097-byte-cap-documented}
|
|
80
|
-
- {operationId: downloadLiteraturePdf, change: zero-start-open-range-4097-byte-cap-documented}
|
|
81
|
-
- {operationId: downloadCloudDriveFile, change: zero-start-open-range-4097-byte-cap-documented}
|
|
82
|
-
- {operationId: listCurrentUserElnGroups, change: dangling-group-id-null-and-first-group-error-modeled}
|
|
83
|
-
- {operationId: downloadRecordCommentAttachment, change: zero-byte-attachment-download-supported}
|
|
84
|
-
- {operationId: listNotebookRecords, change: local-data-server-success-code-zero-internal-forwarding-code-excluded}
|
|
85
|
-
- {operationId: exportRecords, change: local-data-server-success-code-zero-internal-forwarding-code-excluded}
|
|
86
|
-
- {operationId: searchRecordData, change: local-data-server-success-code-zero-internal-forwarding-code-excluded}
|
|
87
|
-
- {operationId: listDeletedRecordPage, change: local-data-server-success-code-zero-internal-forwarding-code-excluded}
|
|
88
|
-
- {operationId: copyRecord, change: local-data-server-success-code-zero-internal-forwarding-code-excluded}
|
|
89
|
-
- {operationId: addRecordRelation, change: local-data-server-success-code-zero-internal-forwarding-code-excluded}
|
|
90
|
-
- {operationId: deleteRecordRelation, change: local-data-server-success-code-zero-internal-forwarding-code-excluded}
|
|
91
|
-
- {operationId: saveRecordComment, change: local-data-server-success-code-zero-internal-forwarding-code-excluded}
|
|
92
|
-
- {operationId: deleteRecordComment, change: local-data-server-success-code-zero-internal-forwarding-code-excluded}
|
|
93
|
-
- {operationId: downloadRecordAttachment, change: remote-data-server-download-excluded-pending-proxy-verification}
|
|
94
|
-
- {operationId: downloadRecordCommentAttachment, change: remote-data-server-download-excluded-pending-proxy-verification}
|
|
67
|
+
- {operationId: readRecord, change: public-only-body-access-and-source-record-identity-documented}
|
|
68
|
+
- {operationId: listPublicNotebookRecords, change: source-record-identifiers-distinguished-from-public-association-id}
|
|
95
69
|
x-consumer-evidence:
|
|
96
70
|
- repository: /home/tnzhu/projects/matelab-mcp
|
|
97
71
|
commit: 94a129b2df0ef0fbcf179c10550fb7c9a5dd2e6f
|
|
@@ -2054,6 +2028,17 @@ paths:
|
|
|
2054
2028
|
operationId: listPublicNotebookRecords
|
|
2055
2029
|
tags: [Records]
|
|
2056
2030
|
summary: List records in a visible public notebook
|
|
2031
|
+
description: |
|
|
2032
|
+
Authentication is required even for a globally visible public directory.
|
|
2033
|
+
`eln` is the public directory's name. Visibility comes from a global
|
|
2034
|
+
directory (`eln_public.userid=0`) or the caller's directory membership;
|
|
2035
|
+
owning or sharing the source notebook is not required.
|
|
2036
|
+
To read an item's current body, call `readRecord` with query
|
|
2037
|
+
`eln=items[n].eln_id&id=items[n].item_id&version=0`. The directory's own
|
|
2038
|
+
id and each returned association `id` are different identities and must
|
|
2039
|
+
not be substituted. `uid` is nullable association metadata, not a body
|
|
2040
|
+
selector. An association can survive a missing source notebook or record;
|
|
2041
|
+
listing it does not guarantee that its body can still be read.
|
|
2057
2042
|
security: [{bearerAuth: []}]
|
|
2058
2043
|
requestBody:
|
|
2059
2044
|
required: true
|
|
@@ -2092,6 +2077,8 @@ paths:
|
|
|
2092
2077
|
- {id: 52, item_id: 9002, eln_id: 12, uid: null, title: null, comm: null, locked: false, eln_name: null, userid: null, username: null, email: null, owner: false}
|
|
2093
2078
|
error:
|
|
2094
2079
|
value: {code: 2, msg: Public notebook not found, errcode: 2, errmsg: Public notebook not found}
|
|
2080
|
+
authenticationError:
|
|
2081
|
+
value: {code: 1, msg: Authentication required, errcode: 1, errmsg: Authentication required}
|
|
2095
2082
|
/eln_items/items:
|
|
2096
2083
|
get:
|
|
2097
2084
|
operationId: listNotebookRecordPage
|
|
@@ -2676,19 +2663,37 @@ paths:
|
|
|
2676
2663
|
operation uses query parameters. Reading records writes an audit-log entry;
|
|
2677
2664
|
that incidental access logging does not turn the resource read into a
|
|
2678
2665
|
mutation operation.
|
|
2666
|
+
Public-record bodies use this same operation and `ItemViewResponse`.
|
|
2667
|
+
After `listPublicNotebookRecords`, pass that item's `eln_id` as `eln` and
|
|
2668
|
+
`item_id` as `id`; keep them paired and use `version=0` for current content.
|
|
2669
|
+
Do not substitute the public directory id, the public association's `id`,
|
|
2670
|
+
or its nullable `uid`.
|
|
2671
|
+
Authentication is still required. For a caller with no source-notebook
|
|
2672
|
+
owner/share grant, an exact `(eln_id, item_id)` association in a global
|
|
2673
|
+
public directory or one they belong to grants record-level read access.
|
|
2674
|
+
This does not grant ownership or access to other records in that notebook.
|
|
2675
|
+
Global public access is read-only; a member directory can separately
|
|
2676
|
+
grant editing through the association's `editable` setting.
|
|
2677
|
+
`modules` contains the canonical body or the existing encrypted descriptor;
|
|
2678
|
+
this endpoint does not decrypt it. The required `uid` field can be null
|
|
2679
|
+
for historical content and is not needed to construct the body request.
|
|
2680
|
+
Missing/invalid authentication returns business code 1; denied access or
|
|
2681
|
+
an invalid current-record identity returns code 2, both over HTTP 200.
|
|
2682
|
+
Historical reads must use a version from this record's own `modify_log`;
|
|
2683
|
+
the Provider does not enforce historical-version ownership (PVD-005).
|
|
2679
2684
|
security:
|
|
2680
2685
|
- bearerAuth: []
|
|
2681
2686
|
parameters:
|
|
2682
2687
|
- name: eln
|
|
2683
2688
|
in: query
|
|
2684
2689
|
required: true
|
|
2685
|
-
description:
|
|
2690
|
+
description: Source notebook database identifier; use PublicRecordItem.eln_id for a public record, not the public directory id.
|
|
2686
2691
|
schema: {type: integer, minimum: 1}
|
|
2687
2692
|
example: 12
|
|
2688
2693
|
- name: id
|
|
2689
2694
|
in: query
|
|
2690
2695
|
required: true
|
|
2691
|
-
description:
|
|
2696
|
+
description: Source record database identifier; use PublicRecordItem.item_id for a public record, not its association id or UID.
|
|
2692
2697
|
schema: {type: integer, minimum: 1}
|
|
2693
2698
|
example: 9001
|
|
2694
2699
|
- name: version
|
|
@@ -2766,6 +2771,38 @@ paths:
|
|
|
2766
2771
|
template_show: ""
|
|
2767
2772
|
template_text: Calibration Template
|
|
2768
2773
|
template_intro: ""
|
|
2774
|
+
public:
|
|
2775
|
+
summary: Current body for a public-only reader without source-notebook sharing
|
|
2776
|
+
value:
|
|
2777
|
+
code: 0
|
|
2778
|
+
msg: ""
|
|
2779
|
+
errcode: 0
|
|
2780
|
+
errmsg: ""
|
|
2781
|
+
title: Calibration Run
|
|
2782
|
+
keywords: [calibration]
|
|
2783
|
+
modules:
|
|
2784
|
+
- {type: richtext, name: Notes, uid: module-example, data: Sanitized content}
|
|
2785
|
+
template_id: 8
|
|
2786
|
+
datetime_modify: 1767946200000
|
|
2787
|
+
username: Example User
|
|
2788
|
+
modify_log: []
|
|
2789
|
+
uid: REC-EXAMPLE-001
|
|
2790
|
+
current_version: 0
|
|
2791
|
+
item_id: 9001
|
|
2792
|
+
datetime_locked: 0
|
|
2793
|
+
locked: false
|
|
2794
|
+
signs: []
|
|
2795
|
+
signed: false
|
|
2796
|
+
owner: false
|
|
2797
|
+
eln_text: Example Notebook
|
|
2798
|
+
eln_server: localhost
|
|
2799
|
+
eln_id: 12
|
|
2800
|
+
editable: false
|
|
2801
|
+
keyword_eln: [calibration]
|
|
2802
|
+
pdf_enable: true
|
|
2803
|
+
template_show: ""
|
|
2804
|
+
template_text: Calibration Template
|
|
2805
|
+
template_intro: ""
|
|
2769
2806
|
nullable:
|
|
2770
2807
|
summary: Historical record with nullable UID, user, template, and keywords
|
|
2771
2808
|
value:
|
|
@@ -2902,6 +2939,11 @@ paths:
|
|
|
2902
2939
|
msg: No operation permission
|
|
2903
2940
|
errcode: 2
|
|
2904
2941
|
errmsg: No operation permission
|
|
2942
|
+
authenticationError:
|
|
2943
|
+
value: {code: 1, msg: Authentication required, errcode: 1, errmsg: Authentication required}
|
|
2944
|
+
identityError:
|
|
2945
|
+
summary: Source notebook and record identifiers do not identify the same current record
|
|
2946
|
+
value: {code: 2, msg: Illegal operation, errcode: 2, errmsg: Illegal operation}
|
|
2905
2947
|
/eln_url/eln:
|
|
2906
2948
|
get:
|
|
2907
2949
|
operationId: downloadRecordAttachment
|
|
@@ -5677,7 +5719,10 @@ components:
|
|
|
5677
5719
|
type: object
|
|
5678
5720
|
required: [eln]
|
|
5679
5721
|
properties:
|
|
5680
|
-
eln:
|
|
5722
|
+
eln:
|
|
5723
|
+
type: string
|
|
5724
|
+
minLength: 1
|
|
5725
|
+
description: Public directory name, not the source notebook name or numeric id.
|
|
5681
5726
|
additionalProperties: false
|
|
5682
5727
|
CreateRecordRequest:
|
|
5683
5728
|
type: object
|
|
@@ -6214,10 +6259,21 @@ components:
|
|
|
6214
6259
|
type: object
|
|
6215
6260
|
required: [id, item_id, eln_id, uid, title, comm, locked, eln_name, userid, username, email, owner]
|
|
6216
6261
|
properties:
|
|
6217
|
-
id:
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6262
|
+
id:
|
|
6263
|
+
type: integer
|
|
6264
|
+
minimum: 1
|
|
6265
|
+
description: Public association row identifier; not the record id accepted by readRecord.
|
|
6266
|
+
item_id:
|
|
6267
|
+
type: integer
|
|
6268
|
+
minimum: 1
|
|
6269
|
+
description: Source record identifier; pass as readRecord query id together with this row's eln_id.
|
|
6270
|
+
eln_id:
|
|
6271
|
+
type: integer
|
|
6272
|
+
minimum: 1
|
|
6273
|
+
description: Source notebook identifier; pass as readRecord query eln, not the public directory id.
|
|
6274
|
+
uid:
|
|
6275
|
+
type: [string, "null"]
|
|
6276
|
+
description: Nullable UID copied into the association; not required for the numeric readRecord selector.
|
|
6221
6277
|
title: {type: [string, "null"]}
|
|
6222
6278
|
comm: {type: [string, "null"]}
|
|
6223
6279
|
locked: {type: boolean}
|
|
@@ -5,13 +5,13 @@ See the [README](../README.md) for installation and quick start and [CONTEXT.md]
|
|
|
5
5
|
|
|
6
6
|
## Contract
|
|
7
7
|
|
|
8
|
-
The SDK is pinned to the immutable `matelab-spec v2026.10.
|
|
8
|
+
The SDK is pinned to the immutable `matelab-spec v2026.10.2` Contract Release. The sole pin,
|
|
9
9
|
[contracts/matelab-integration-v1.lock.json](../contracts/matelab-integration-v1.lock.json), records peeled commit
|
|
10
|
-
`
|
|
11
|
-
and SHA-256 `
|
|
12
|
-
`
|
|
10
|
+
`1183d6833d88a6904598357321ef2d30cf944339`, source `openapi/matelab-integration-v1.openapi.yaml`, the local snapshot path,
|
|
11
|
+
and SHA-256 `4806fc2e4b4cb4cf94464be6f1b5f574f5a44745bf7cbc03d8644a13e57f8ec9`. The annotated tag object is
|
|
12
|
+
`4ea93c32637a8bcbea51cf6c6fb2da4c316a7134`.
|
|
13
13
|
|
|
14
|
-
The Contract uses Provider source evidence from `3f39d47b7ad43bed618b029a7d9cc51fab852eb6`; v2026.10.
|
|
14
|
+
The Contract uses Provider source evidence from `3f39d47b7ad43bed618b029a7d9cc51fab852eb6`; v2026.10.2 validation ran
|
|
15
15
|
against an isolated test instance and does not establish production behavior. The Front Consumer evidence
|
|
16
16
|
`0c985f5b6da8e8ef4627ca7065cea85e408ae981` informs cross-Consumer capability review but does not define the SDK interface.
|
|
17
17
|
|
|
@@ -65,7 +65,7 @@ callable Contract and raises `MatelabProtocolError` rather than assigning an err
|
|
|
65
65
|
`MatelabTransportError.status_code` remains available for HTTP failures. Provider response bodies, top-level `msg`,
|
|
66
66
|
legacy `errmsg`, opaque `errs`, uncontracted debug fields, and request payloads are not attached to exceptions because
|
|
67
67
|
Provider-authored diagnostics may contain stack traces, credentials, or submitted values.
|
|
68
|
-
The pinned
|
|
68
|
+
The pinned Contract also declares optional code-4 `errs`, but PVD-029 and PCG-010 record opaque array/object
|
|
69
69
|
containers and insufficient evidence for requiredness, cardinality, or exclusion from other codes. The SDK
|
|
70
70
|
therefore keeps those diagnostics wire-only and does not expose `errs` as stable public metadata. A typed detail
|
|
71
71
|
interface remains blocked until a future Contract release can define one from stronger Provider evidence. `retryable`
|
|
@@ -556,6 +556,25 @@ Search and relation order remain unspecified, and no continuation token is inven
|
|
|
556
556
|
directly. Remote data-server forwarding code `10` is rejected because only local data-server results are covered.
|
|
557
557
|
`DeletedRecordPage` does not repeat its input notebook.
|
|
558
558
|
|
|
559
|
+
Read a public entry's current body directly from its ref:
|
|
560
|
+
|
|
561
|
+
```python
|
|
562
|
+
notebooks = await client.notebooks.list()
|
|
563
|
+
entries = await client.records.list_public(notebooks.public[0].ref)
|
|
564
|
+
if entries:
|
|
565
|
+
record = await client.records.read_public(entries[0].ref)
|
|
566
|
+
modules = record.modules
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
`read_public()` returns `PublicRecord`, with the input `PublicRecordRef` as `source` and the same canonical `modules`
|
|
570
|
+
and content metadata as a private read. It uses the paired source notebook and record IDs; the public association ID
|
|
571
|
+
and its nullable, potentially stale UID do not select or validate the body. Authentication is required, and the Provider
|
|
572
|
+
checks current access through a global directory or directory membership and writes a read audit entry (PVD-035).
|
|
573
|
+
This grants no notebook-wide
|
|
574
|
+
access. A listed association may outlive its source, and revoked access or a missing record raises a Provider error.
|
|
575
|
+
Encrypted content remains in `encrypted_modules`, with empty plaintext `modules`. Public reads expose current content;
|
|
576
|
+
historical-version and downloadable-attachment refs remain available through owned/shared record reads.
|
|
577
|
+
|
|
559
578
|
Record creation keeps blank creation and structured import as separate capabilities:
|
|
560
579
|
|
|
561
580
|
```python
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
schema_version: 1
|
|
4
4
|
contract:
|
|
5
5
|
line: integration-v1
|
|
6
|
-
release: v2026.10.
|
|
6
|
+
release: v2026.10.2
|
|
7
7
|
operation_count: 71
|
|
8
8
|
|
|
9
9
|
operations:
|
|
@@ -429,7 +429,7 @@ operations:
|
|
|
429
429
|
domain: records
|
|
430
430
|
state: implemented
|
|
431
431
|
stability: stable
|
|
432
|
-
public_interface: AsyncMatelab.records.read
|
|
432
|
+
public_interface: AsyncMatelab.records.read / AsyncMatelab.records.read_public
|
|
433
433
|
known_issues: [PCG-005, PCG-012, PVD-035, PVD-005, PVD-044]
|
|
434
434
|
|
|
435
435
|
- operation_id: downloadRecordAttachment
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "matelab-python-sdk"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.0a5"
|
|
8
8
|
description = "Reusable async Python client for the Matelab Integration Contract"
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
keywords = ["matelab", "electronic-lab-notebook", "eln", "asyncio", "sdk"]
|
|
@@ -41,6 +41,7 @@ from matelab.domains.records import (
|
|
|
41
41
|
DeletedRecordSummary,
|
|
42
42
|
EncryptedRecordModules,
|
|
43
43
|
ExportedRecord,
|
|
44
|
+
PublicRecord,
|
|
44
45
|
PublicRecordRef,
|
|
45
46
|
PublicRecordSummary,
|
|
46
47
|
Record,
|
|
@@ -145,6 +146,7 @@ __all__ = [
|
|
|
145
146
|
"NotebookShareRef",
|
|
146
147
|
"NotebookShares",
|
|
147
148
|
"NotebookSummary",
|
|
149
|
+
"PublicRecord",
|
|
148
150
|
"PublicRecordRef",
|
|
149
151
|
"PublicRecordSummary",
|
|
150
152
|
"PublicationAuthorCandidate",
|
|
@@ -11,7 +11,7 @@ from pydantic import ConfigDict, Field, RootModel, SecretStr, StrictBool, Strict
|
|
|
11
11
|
|
|
12
12
|
from matelab._wire_validation import WireModel
|
|
13
13
|
|
|
14
|
-
OPENAPI_INFO_VERSION = "2026.10.
|
|
14
|
+
OPENAPI_INFO_VERSION = "2026.10.2"
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class ResponseEnvelope(WireModel):
|
|
@@ -1433,6 +1433,9 @@ class DeleteNotebookShareRequest(WireModel):
|
|
|
1433
1433
|
class PublicRecordListRequest(WireModel):
|
|
1434
1434
|
model_config = ConfigDict(extra="forbid")
|
|
1435
1435
|
eln: Annotated[str, Field(min_length=1)]
|
|
1436
|
+
"""
|
|
1437
|
+
Public directory name, not the source notebook name or numeric id.
|
|
1438
|
+
"""
|
|
1436
1439
|
|
|
1437
1440
|
|
|
1438
1441
|
class CreateRecordRequest(WireModel):
|
|
@@ -2047,9 +2050,21 @@ class DeleteRecordsRequest(WireModel):
|
|
|
2047
2050
|
|
|
2048
2051
|
class PublicRecordItem(WireModel):
|
|
2049
2052
|
id: Annotated[StrictInt, Field(ge=1)]
|
|
2053
|
+
"""
|
|
2054
|
+
Public association row identifier; not the record id accepted by readRecord.
|
|
2055
|
+
"""
|
|
2050
2056
|
item_id: Annotated[StrictInt, Field(ge=1)]
|
|
2057
|
+
"""
|
|
2058
|
+
Source record identifier; pass as readRecord query id together with this row's eln_id.
|
|
2059
|
+
"""
|
|
2051
2060
|
eln_id: Annotated[StrictInt, Field(ge=1)]
|
|
2061
|
+
"""
|
|
2062
|
+
Source notebook identifier; pass as readRecord query eln, not the public directory id.
|
|
2063
|
+
"""
|
|
2052
2064
|
uid: Annotated[str | None, Field(...)]
|
|
2065
|
+
"""
|
|
2066
|
+
Nullable UID copied into the association; not required for the numeric readRecord selector.
|
|
2067
|
+
"""
|
|
2053
2068
|
title: Annotated[str | None, Field(...)]
|
|
2054
2069
|
comm: Annotated[str | None, Field(...)]
|
|
2055
2070
|
locked: StrictBool
|
|
@@ -320,6 +320,8 @@ class RecordSearchMatch:
|
|
|
320
320
|
|
|
321
321
|
@dataclass(frozen=True, slots=True)
|
|
322
322
|
class PublicRecordRef:
|
|
323
|
+
"""Public association metadata with the paired source IDs used for body reads."""
|
|
324
|
+
|
|
323
325
|
public_entry_id: int
|
|
324
326
|
source_notebook_id: int
|
|
325
327
|
source_record_id: int
|
|
@@ -587,6 +589,20 @@ class EncryptedRecordModules:
|
|
|
587
589
|
extra: Mapping[str, JsonValue] = field(default_factory=dict[str, JsonValue], repr=False)
|
|
588
590
|
|
|
589
591
|
|
|
592
|
+
@dataclass(frozen=True, slots=True)
|
|
593
|
+
class PublicRecord:
|
|
594
|
+
"""Current content read through a public record association."""
|
|
595
|
+
|
|
596
|
+
source: PublicRecordRef
|
|
597
|
+
title: str | None
|
|
598
|
+
keywords: tuple[str, ...] | None
|
|
599
|
+
modules: tuple[dict[str, JsonValue], ...]
|
|
600
|
+
template_id: int | None
|
|
601
|
+
modified_at_ms: int
|
|
602
|
+
author_name: str | None
|
|
603
|
+
encrypted_modules: EncryptedRecordModules | None = None
|
|
604
|
+
|
|
605
|
+
|
|
590
606
|
@dataclass(frozen=True, slots=True)
|
|
591
607
|
class Record:
|
|
592
608
|
"""Current or historical record content with safe attachment references."""
|
|
@@ -732,6 +748,18 @@ def _contains_native_attachment_reference(value: JsonValue) -> bool:
|
|
|
732
748
|
return False
|
|
733
749
|
|
|
734
750
|
|
|
751
|
+
def _record_modules(
|
|
752
|
+
response: wire_models.ItemViewResponse,
|
|
753
|
+
) -> tuple[tuple[dict[str, JsonValue], ...], EncryptedRecordModules | None]:
|
|
754
|
+
if isinstance(response.modules, wire_models.EncryptedRecordModules):
|
|
755
|
+
return (), EncryptedRecordModules(
|
|
756
|
+
ciphertext=response.modules.data,
|
|
757
|
+
algorithm=response.modules.encrypt,
|
|
758
|
+
extra=MappingProxyType(cast(dict[str, JsonValue], response.modules.model_extra or {})),
|
|
759
|
+
)
|
|
760
|
+
return tuple(item.model_dump(mode="json") for item in response.modules), None
|
|
761
|
+
|
|
762
|
+
|
|
735
763
|
def _record_attachment_refs(
|
|
736
764
|
modules: Sequence[Mapping[str, JsonValue]], *, source: RecordLocator, version: RecordVersionRef | None
|
|
737
765
|
) -> tuple[RecordAttachmentRef, ...]:
|
|
@@ -1708,6 +1736,28 @@ class Records:
|
|
|
1708
1736
|
)
|
|
1709
1737
|
return await self._transport.stream(_DOWNLOAD_COMMENT_ATTACHMENT, payload=request, byte_range=byte_range)
|
|
1710
1738
|
|
|
1739
|
+
async def read_public(self, source: PublicRecordRef) -> PublicRecord:
|
|
1740
|
+
"""Read current content using a public entry's source notebook and record IDs."""
|
|
1741
|
+
request = validate_request(
|
|
1742
|
+
wire_models.ReadRecordParametersQuery,
|
|
1743
|
+
{"eln": source.source_notebook_id, "id": source.source_record_id, "version": 0},
|
|
1744
|
+
"Public record source identity does not satisfy the Integration Contract.",
|
|
1745
|
+
)
|
|
1746
|
+
response = await self._transport.request(_READ_RECORD, payload=request)
|
|
1747
|
+
modules, encrypted_modules = _record_modules(response)
|
|
1748
|
+
return PublicRecord(
|
|
1749
|
+
source=source,
|
|
1750
|
+
title=response.title,
|
|
1751
|
+
keywords=tuple(response.keywords) if response.keywords is not None else None,
|
|
1752
|
+
modules=modules,
|
|
1753
|
+
template_id=response.template_id,
|
|
1754
|
+
modified_at_ms=response.datetime_modify,
|
|
1755
|
+
author_name=response.username.root
|
|
1756
|
+
if response.username is not None and isinstance(response.username.root, str)
|
|
1757
|
+
else None,
|
|
1758
|
+
encrypted_modules=encrypted_modules,
|
|
1759
|
+
)
|
|
1760
|
+
|
|
1711
1761
|
async def read(self, source: RecordLocator | RecordVersionRef) -> Record:
|
|
1712
1762
|
if isinstance(source, RecordVersionRef):
|
|
1713
1763
|
version = source
|
|
@@ -1719,16 +1769,7 @@ class Records:
|
|
|
1719
1769
|
record_source, version_id=version.version_id if version is not None else None
|
|
1720
1770
|
)
|
|
1721
1771
|
|
|
1722
|
-
encrypted_modules =
|
|
1723
|
-
if isinstance(response.modules, wire_models.EncryptedRecordModules):
|
|
1724
|
-
encrypted_modules = EncryptedRecordModules(
|
|
1725
|
-
ciphertext=response.modules.data,
|
|
1726
|
-
algorithm=response.modules.encrypt,
|
|
1727
|
-
extra=MappingProxyType(cast(dict[str, JsonValue], response.modules.model_extra or {})),
|
|
1728
|
-
)
|
|
1729
|
-
modules: tuple[dict[str, JsonValue], ...] = ()
|
|
1730
|
-
else:
|
|
1731
|
-
modules = tuple(item.model_dump(mode="json") for item in response.modules)
|
|
1772
|
+
modules, encrypted_modules = _record_modules(response)
|
|
1732
1773
|
return Record(
|
|
1733
1774
|
source=record_source,
|
|
1734
1775
|
version=version,
|
|
@@ -7,9 +7,13 @@ from httpx2 import AsyncClient, MockTransport, Request, Response
|
|
|
7
7
|
from matelab import (
|
|
8
8
|
AsyncMatelab,
|
|
9
9
|
Identity,
|
|
10
|
+
MatelabAuthenticationError,
|
|
11
|
+
MatelabError,
|
|
10
12
|
MatelabProtocolError,
|
|
13
|
+
MatelabProviderError,
|
|
11
14
|
MatelabUsageError,
|
|
12
15
|
NotebookRef,
|
|
16
|
+
PublicRecordRef,
|
|
13
17
|
RecordLocator,
|
|
14
18
|
RecordRef,
|
|
15
19
|
RecordValueUpdate,
|
|
@@ -127,6 +131,107 @@ async def test_list_records_omits_owned_defaults_and_accepts_empty_example() ->
|
|
|
127
131
|
assert records.trash_count == 0
|
|
128
132
|
|
|
129
133
|
|
|
134
|
+
@pytest.mark.asyncio
|
|
135
|
+
@pytest.mark.parametrize("association_uid", [None, "outdated-association-uid"])
|
|
136
|
+
async def test_read_public_record_uses_source_ids_and_maps_current_body(
|
|
137
|
+
association_uid: str | None,
|
|
138
|
+
contract_response_example: Callable[[str, str], dict[str, object]],
|
|
139
|
+
provider_session: Session,
|
|
140
|
+
) -> None:
|
|
141
|
+
payload = contract_response_example("readRecord", "public")
|
|
142
|
+
payload["future_response_field"] = True
|
|
143
|
+
calls: list[Request] = []
|
|
144
|
+
|
|
145
|
+
async def handler(request: Request) -> Response:
|
|
146
|
+
calls.append(request)
|
|
147
|
+
assert request.method == "GET"
|
|
148
|
+
assert request.url.path == "/api/eln_items/item_view"
|
|
149
|
+
assert dict(request.url.params) == {"eln": "12", "id": "9001", "version": "0"}
|
|
150
|
+
assert request.headers["Authorization"] == "Bearer access"
|
|
151
|
+
assert request.content == b""
|
|
152
|
+
return Response(200, json=payload)
|
|
153
|
+
|
|
154
|
+
source = PublicRecordRef(
|
|
155
|
+
public_entry_id=51, source_notebook_id=12, source_record_id=9001, record_uid=association_uid
|
|
156
|
+
)
|
|
157
|
+
async with AsyncClient(base_url="https://matelab.example/api", transport=MockTransport(handler)) as http_client:
|
|
158
|
+
client = AsyncMatelab("https://ignored.example", session=provider_session, http_client=http_client)
|
|
159
|
+
record = await client.records.read_public(source)
|
|
160
|
+
|
|
161
|
+
assert len(calls) == 1
|
|
162
|
+
assert record.source == source
|
|
163
|
+
assert record.title == "Calibration Run"
|
|
164
|
+
assert record.keywords == ("calibration",)
|
|
165
|
+
assert record.modules == (
|
|
166
|
+
{
|
|
167
|
+
"type": "richtext",
|
|
168
|
+
"name": "Notes",
|
|
169
|
+
"uid": "module-example",
|
|
170
|
+
"data": "Sanitized content",
|
|
171
|
+
"language": None,
|
|
172
|
+
"style": None,
|
|
173
|
+
},
|
|
174
|
+
)
|
|
175
|
+
assert record.template_id == 8
|
|
176
|
+
assert record.modified_at_ms == payload["datetime_modify"]
|
|
177
|
+
assert record.author_name == "Example User"
|
|
178
|
+
assert record.encrypted_modules is None
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@pytest.mark.asyncio
|
|
182
|
+
async def test_read_public_record_preserves_encryption_and_nullable_metadata(
|
|
183
|
+
contract_response_example: Callable[[str, str], dict[str, object]], provider_session: Session
|
|
184
|
+
) -> None:
|
|
185
|
+
payload = contract_response_example("readRecord", "public")
|
|
186
|
+
payload.update(
|
|
187
|
+
title=None,
|
|
188
|
+
keywords=None,
|
|
189
|
+
template_id=None,
|
|
190
|
+
username=None,
|
|
191
|
+
modules={"data": "opaque-ciphertext", "encrypt": "CBC", "future_field": {"version": 1}},
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
async def handler(_: Request) -> Response:
|
|
195
|
+
return Response(200, json=payload)
|
|
196
|
+
|
|
197
|
+
source = PublicRecordRef(public_entry_id=51, source_notebook_id=12, source_record_id=9001, record_uid=None)
|
|
198
|
+
async with AsyncClient(base_url="https://matelab.example/api", transport=MockTransport(handler)) as http_client:
|
|
199
|
+
client = AsyncMatelab("https://ignored.example", session=provider_session, http_client=http_client)
|
|
200
|
+
record = await client.records.read_public(source)
|
|
201
|
+
|
|
202
|
+
assert record.title is None
|
|
203
|
+
assert record.keywords is None
|
|
204
|
+
assert record.template_id is None
|
|
205
|
+
assert record.author_name is None
|
|
206
|
+
assert record.modules == ()
|
|
207
|
+
descriptor = record.encrypted_modules
|
|
208
|
+
assert descriptor is not None
|
|
209
|
+
assert descriptor.ciphertext == "opaque-ciphertext"
|
|
210
|
+
assert descriptor.algorithm == "CBC"
|
|
211
|
+
assert descriptor.extra == {"future_field": {"version": 1}}
|
|
212
|
+
assert "opaque-ciphertext" not in repr(record)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@pytest.mark.asyncio
|
|
216
|
+
@pytest.mark.parametrize(
|
|
217
|
+
("example", "error_type"), [("error", MatelabProviderError), ("authenticationError", MatelabAuthenticationError)]
|
|
218
|
+
)
|
|
219
|
+
async def test_read_public_record_raises_provider_errors(
|
|
220
|
+
example: str,
|
|
221
|
+
error_type: type[MatelabError],
|
|
222
|
+
contract_response_example: Callable[[str, str], dict[str, object]],
|
|
223
|
+
provider_session: Session,
|
|
224
|
+
) -> None:
|
|
225
|
+
async def handler(_: Request) -> Response:
|
|
226
|
+
return Response(200, json=contract_response_example("readRecord", example))
|
|
227
|
+
|
|
228
|
+
source = PublicRecordRef(public_entry_id=51, source_notebook_id=12, source_record_id=9001, record_uid=None)
|
|
229
|
+
async with AsyncClient(base_url="https://matelab.example/api", transport=MockTransport(handler)) as http_client:
|
|
230
|
+
client = AsyncMatelab("https://ignored.example", session=provider_session, http_client=http_client)
|
|
231
|
+
with pytest.raises(error_type):
|
|
232
|
+
_ = await client.records.read_public(source)
|
|
233
|
+
|
|
234
|
+
|
|
130
235
|
@pytest.mark.asyncio
|
|
131
236
|
async def test_read_current_record_maps_pinned_contract_success_example(
|
|
132
237
|
contract_response_example: Callable[[str, str], dict[str, object]],
|
|
@@ -11,6 +11,8 @@ from matelab import (
|
|
|
11
11
|
NotebookCollection,
|
|
12
12
|
NotebookRef,
|
|
13
13
|
PublicationMetadataCandidate,
|
|
14
|
+
PublicRecord,
|
|
15
|
+
PublicRecordSummary,
|
|
14
16
|
Record,
|
|
15
17
|
RecordAttachmentRef,
|
|
16
18
|
RecordAttachmentReplacement,
|
|
@@ -94,6 +96,12 @@ async def consume_record(client: AsyncMatelab, source: RecordLocator) -> Record:
|
|
|
94
96
|
return record
|
|
95
97
|
|
|
96
98
|
|
|
99
|
+
async def consume_public_record(client: AsyncMatelab, notebook: NotebookRef) -> PublicRecord | None:
|
|
100
|
+
entries: tuple[PublicRecordSummary, ...] = await client.records.list_public(notebook)
|
|
101
|
+
record: PublicRecord | None = await client.records.read_public(entries[0].ref) if entries else None
|
|
102
|
+
return record
|
|
103
|
+
|
|
104
|
+
|
|
97
105
|
async def consume_record_attachment_intents(
|
|
98
106
|
client: AsyncMatelab,
|
|
99
107
|
source: RecordLocator,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/scripts/check_installed_package.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/_generated/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/_attachment_content.py
RENAMED
|
File without changes
|
{matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/src/matelab/domains/cloud_drive.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/provider/test_provider_smoke.py
RENAMED
|
File without changes
|
{matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_async_attachment_upload.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_comments_and_downloads.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.2.0a4 → matelab_python_sdk-0.2.0a5}/tests/test_uploads_and_literature.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|