From b9244fe1004ecbc2056e478097e0a04289ab57b8 Mon Sep 17 00:00:00 2001 From: Ranjiwei <32759763+r350178982@users.noreply.github.com> Date: Wed, 9 Sep 2026 16:16:57 +0800 Subject: [PATCH 1/2] update --- base_operations.yaml | 190 ++++++++++++++++++++----------------------- intro/changelog.md | 6 ++ 2 files changed, 96 insertions(+), 100 deletions(-) diff --git a/base_operations.yaml b/base_operations.yaml index f8738fb..28c4be5 100644 --- a/base_operations.yaml +++ b/base_operations.yaml @@ -78,6 +78,26 @@ components: description: The number of results that should be returned. If no value is provided, 25 results will be returned. example: 25 required: false + per_page_10: + name: per_page + in: query + schema: + type: integer + minimum: 1 + default: 10 + description: The number of results that should be returned. If no value is provided, 10 results will be returned. + example: 10 + required: false + per_page_100: + name: per_page + in: query + schema: + type: integer + minimum: 1 + default: 100 + description: The number of results that should be returned. If no value is provided, 100 results will be returned. + example: 100 + required: false op_type: name: op_type description: >- @@ -2339,9 +2359,17 @@ components: create_row_comment: type: object properties: + row_id: + $ref: "#/components/parameters/row_id" + table_id: + $ref: "#/components/parameters/table_id" comment: type: string example: "Let's discuss this tomorrow" + required: + - row_id + - table_id + - comment generate_snapshot: type: object @@ -2444,7 +2472,7 @@ paths: type: object example: error_message: invalid token - /api-gateway/api/v2/dtables/{base_uuid}/related-users/: + /api/v2.1/dtables/{base_uuid}/related-users/: get: tags: - Base Info @@ -2466,12 +2494,15 @@ paths: user_list: - email: 244b430060f54bb4afa2c2cb7369d244@auth.local name: Ginger Ale - contact_email: gingerale@example.com avatar_url: https://cloud.seatable.io/media/avatars/default.png + id_in_org: W-00026 + name_pinyin: ginger'ale + app_user_list: - email: 8cb2a6da65687600f42905bf1647fd3f@auth.local name: Jasmin Tee - contact_email: jasmintee@example.com avatar_url: https://cloud.seatable.io/media/avatars/default.png + id_in_org: W-00027 + name_pinyin: jasmin'tee # Rows /api-gateway/api/v2/dtables/{base_uuid}/sql/: @@ -4292,7 +4323,7 @@ paths: success:true # Row Comments - /api-gateway/api/v2/dtables/{base_uuid}/comments/: + /api/v2.1/dtables/{base_uuid}/comments/: get: tags: - Row Comments @@ -4305,6 +4336,8 @@ paths: - BaseTokenAuth: [] parameters: - $ref: "#/components/parameters/base_uuid" + - $ref: "#/components/parameters/page" + - $ref: "#/components/parameters/per_page_10" - $ref: "#/components/parameters/row_id" responses: "200": @@ -4312,34 +4345,19 @@ paths: content: application/json: schema: - type: array - items: - type: object + type: object example: - - id: 1 - author: 28d006e7d1754bb4afa2c2cb7369d244@auth.local - comment: This is good! - dtable_uuid: 650d8a0d-7e27-46a8-8b18-6cc6374yf557 - row_id: NAu2B3OcRG6UrWagL-9naA - created_at: "2021-01-15T13:35:26.000Z" - updated_at: "2021-01-15T13:35:26.000Z" - resolved: 0 - - id: 2 - author: 28d006e7d1754bb4afa2c2cb7369d244@auth.local - comment: Go online tomorrow? - dtable_uuid: 650d8a0d-7e27-46a8-8b18-6cc6374yf557 - row_id: NAu2B3OcRG6UrWagL-9naA - created_at: "2021-01-15T13:52:48.000Z" - updated_at: "2021-01-15T13:52:48.000Z" - resolved: 0 - - id: 3 - author: 8cb2a6da1928374ba42905bf1647fd3f@auth.local - comment: Agreed! - dtable_uuid: 650d8a0d-7e27-46a8-8b18-6cc6374yf557 - row_id: NAu2B3OcRG6UrWagL-9naA - created_at: "2021-01-15T13:53:13.000Z" - updated_at: "2021-01-15T13:53:13.000Z" - resolved: 1 + comment_list: + - id: 1 + author: 28d006e7d1754bb4afa2c2cb7369d244@auth.local + comment: This is good! + dtable_uuid: 650d8a0d-7e27-46a8-8b18-6cc6374yf557 + row_id: NAu2B3OcRG6UrWagL-9naA + created_at: "2021-01-15T13:35:26.000Z" + updated_at: "2021-01-15T13:35:26.000Z" + detail: null + resolved: false + count: 1 post: tags: - Row Comments @@ -4356,8 +4374,6 @@ paths: $ref: "#/components/schemas/create_row_comment" parameters: - $ref: "#/components/parameters/base_uuid" - - $ref: "#/components/parameters/table_id" - - $ref: "#/components/parameters/row_id" responses: "200": description: OK @@ -4367,7 +4383,7 @@ paths: type: object example: success:true - /api-gateway/api/v2/dtables/{base_uuid}/comments/{comment_id}/: + /api/v2.1/dtables/{base_uuid}/comments/{comment_id}/: delete: tags: - Row Comments @@ -4388,17 +4404,31 @@ paths: type: object example: success: true - get: + put: tags: - Row Comments - summary: Get Comment - operationId: getComment - description: Get the details of a certain comment with its ID. + summary: Resolve Comment + operationId: resolveComment + description: Update the resolved status of a certain comment by its ID. security: - BaseTokenAuth: [] parameters: - $ref: "#/components/parameters/base_uuid" - $ref: "#/components/parameters/comment_id" + requestBody: + content: + application/json: + schema: + type: object + properties: + options: + type: object + properties: + resolved: + type: integer + enum: [0, 1] + required: + - options responses: "200": description: OK @@ -4406,33 +4436,9 @@ paths: application/json: schema: type: object - properties: - id: - type: integer - author: - type: string - comment: - type: string - dtable_uuid: - type: string - row_id: - type: string - created_at: - type: string - updated_at: - type: string - resolved: - type: integer example: - id: 1 - author: 12345678d17570046f03c2cb7369d244@auth.local - comment: Here is my email address - dtable_uuid: 12345678-7e27-46a8-8b18-6cc6374yf557 - row_id: NAu2B3OcRG6UrWagL-9naA - created_at: "2021-01-15T13:52:48.000Z" - updated_at: "2021-01-15T13:52:48.000Z" - resolved: 0 - /api-gateway/api/v2/dtables/{base_uuid}/comments-count/: + success: true + /api/v2.1/dtables/{base_uuid}/comments-count/: get: tags: - Row Comments @@ -4455,37 +4461,6 @@ paths: type: object example: count: 3 - /api-gateway/api/v2/dtables/{base_uuid}/comments-within-days/: - get: - tags: - - Row Comments - summary: List Comments within Days - operationId: listCommentsWithinDays - description: >- - List all the comments in a base within a given number of days before - today. - security: - - BaseTokenAuth: [] - parameters: - - $ref: "#/components/parameters/base_uuid" - - $ref: "#/components/parameters/days" - responses: - "200": - description: OK - content: - application/json: - schema: - type: object - example: - comments: - - id: 1 - author: 123456786569491ba42905bf1647fd3f@auth.local - comment: Let's discuss this tomorrow - dtable_uuid: 12345678-7e27-46a8-8b18-6cc6f3db2057 - row_id: Qtf7xPmoRaiFyQPO1aNTjA - created_at: "2021-03-09T15:54:22.000Z" - updated_at: "2021-03-09T15:54:22.000Z" - resolved: 0 /api/v2.1/dtables/{base_uuid}/rows-comments-num/: get: tags: @@ -4510,7 +4485,7 @@ paths: C0LWRVCHT0OoAGjoXBuKMA: 1 # Notifications (Base) - /api-gateway/api/v2/dtables/{base_uuid}/notifications/: + /api/v2.1/dtables/{base_uuid}/notifications/: get: tags: - Notifications @@ -4521,6 +4496,8 @@ paths: - BaseTokenAuth: [] parameters: - $ref: "#/components/parameters/base_uuid" + - $ref: "#/components/parameters/page" + - $ref: "#/components/parameters/per_page" responses: "200": description: OK @@ -4532,6 +4509,7 @@ paths: notification_list: - id: 200 username: 123456786569491ba42905bf1647fd3f@auth.local + dtable_uuid: 650d8a0d-7e27-46a8-8b18-6cc6374yf557 msg_type: row_comment created_at: "2021-02-25T10:38:14.000Z" detail: @@ -4665,7 +4643,7 @@ paths: success: true # Activities & Logs - /api-gateway/api/v2/dtables/{base_uuid}/operations/: + /api/v2.1/dtables/{base_uuid}/operation-logs/: get: tags: - Activities & Logs @@ -4676,6 +4654,7 @@ paths: - BaseTokenAuth: [] parameters: - $ref: "#/components/parameters/page" + - $ref: "#/components/parameters/per_page_100" - $ref: "#/components/parameters/base_uuid" responses: "200": @@ -4686,7 +4665,9 @@ paths: type: object example: operations: - - author: 12345678d1754bb4afa2c2cb7369d244@auth.local + - id: 118 + dtable_uuid: 650d8a0d-7e27-46a8-8b18-6cc6374yf557 + author: 12345678d1754bb4afa2c2cb7369d244@auth.local app: null op_time: 1610981745927 operation: >- @@ -4704,7 +4685,7 @@ paths: operation: >- {"op_type":"delete_column","table_id":"0000","column_key":"jQyv","old_column":{"rowType":"header","key":"jQyv","type":null,"name":null,"editable":true,"width":200,"resizable":true,"draggable":true,"data":null,"permission_type":"","permitted_users":[],"editor":{"key":null,"ref":null,"props":{},"_owner":null},"formatter":null,"left":480,"idx":3},"upper_column_key":"J2mq"} op_id: 116 - /api-gateway/api/v2/dtables/{base_uuid}/activities/: + /api/v2.1/dtables/{base_uuid}/row-activities/: get: tags: - Activities & Logs @@ -4716,7 +4697,7 @@ paths: parameters: - $ref: "#/components/parameters/row_id" - $ref: "#/components/parameters/page" - - $ref: "#/components/parameters/per_page" + - $ref: "#/components/parameters/per_page_10" - $ref: "#/components/parameters/base_uuid" responses: "200": @@ -4729,14 +4710,17 @@ paths: activities: - id: 6782 dtable_uuid: a57b56d3-1cc5-4ebd-8a6c-a1b28ac3dbdf + row_count: 1 row_id: YMIviMeERQCUiQhPPqo6Gw op_user: 0ef256cb715841dd81b147b2530c2904@auth.local + op_app: null op_type: modify_row op_time: "2021-01-14T09:01:57.000Z" detail: table_id: "0000" table_name: Table1 row_name: Meng + row_name_option: "" row_data: - column_key: BydO column_name: Date @@ -4747,14 +4731,17 @@ paths: old_value: "2020-08-16" - id: 6778 dtable_uuid: a57b56d3-1cc5-4ebd-8a6c-a1b28ac3dbdf + row_count: 1 row_id: YMIviMeERQCUiQhPPqo6Gw op_user: 0ef256cb715841dd81b147b2530c2904@auth.local + op_app: null op_type: modify_row op_time: "2021-01-14T08:56:53.000Z" detail: table_id: "0000" table_name: Table1 row_name: Meng + row_name_option: "" row_data: - column_key: "0000" column_name: Name @@ -4771,14 +4758,17 @@ paths: old_value: "" - id: 5960 dtable_uuid: a57b56d3-1cc5-4ebd-8a6c-a1b28ac3dbdf + row_count: 1 row_id: YMIviMeERQCUiQhPPqo6Gw op_user: 0ef256cb715841dd81b147b2530c2904@auth.local + op_app: null op_type: insert_row op_time: "2020-11-18T12:42:14.000Z" detail: table_id: "0000" table_name: Table1 row_name: "" + row_name_option: "" row_data: - column_key: "0000" column_name: Name diff --git a/intro/changelog.md b/intro/changelog.md index a4a0a63..7fecbb0 100644 --- a/intro/changelog.md +++ b/intro/changelog.md @@ -14,6 +14,12 @@ slug: changelog Listed below are all the changes to the SeaTable API. Each date corresponds to a new version of SeaTable Server Enterprise Edition. If you’re looking for changes beyond the API, see the SeaTable [Changelog](https://seatable.com/changelog) or check out the [SeaTable Blog](https://seatable.com/blog) for detailed release notes. +## Version 7.0 + +> 🚧 Breaking changes +> +> - The deprecated API Gateway endpoints for base activity logs, row activities, row comments, collaborators, and base notifications were moved to `/api/v2.1/dtables/{base_uuid}/`. `GET /api-gateway/api/v2/dtables/{base_uuid}/comments-within-days/` was removed; use `GET /api/v2.1/dtables/{base_uuid}/comments/?row_id={row_id}` instead. + ## Version 6.2 (21.07.2026) > 🚧 Breaking changes From e5679c1e6d6fa58d7c236c627ab6552096cb4a3e Mon Sep 17 00:00:00 2001 From: Ranjiwei <32759763+r350178982@users.noreply.github.com> Date: Wed, 9 Sep 2026 16:31:23 +0800 Subject: [PATCH 2/2] update test --- base_operations.yaml | 47 ++++++++++--------- tests/test_comments.py | 90 ++++++++----------------------------- tests/test_metadata.py | 3 +- tests/test_notifications.py | 19 +++----- 4 files changed, 54 insertions(+), 105 deletions(-) diff --git a/base_operations.yaml b/base_operations.yaml index 28c4be5..62cb498 100644 --- a/base_operations.yaml +++ b/base_operations.yaml @@ -4518,22 +4518,12 @@ paths: row_id: fS8qtN6FQ1uPOaNAC0Locw comment: Did you see this? seen: 1 - put: + delete: tags: - Notifications - summary: Mark Base Notifications as seen - operationId: markBaseNotificationsAsSeen - description: Use this request to mark all the notifications as read. - requestBody: - content: - application/x-www-form-urlencoded: - schema: - type: object - properties: - seen: - type: boolean - description: true or false for read or unread. Otherwise invalid. - example: true + summary: Delete Base Notifications + operationId: deleteBaseNotifications + description: Delete all the notifications in the current base irrevocably. security: - BaseTokenAuth: [] parameters: @@ -4547,12 +4537,26 @@ paths: type: object example: success: true - delete: + /api-gateway/api/v2/dtables/{base_uuid}/notifications/: + put: tags: - Notifications - summary: Delete Base Notifications - operationId: deleteBaseNotifications - description: Delete all the notifications in the current base irrevocably. + summary: Mark Base Notifications as seen + operationId: markBaseNotificationsAsSeen + description: Use this request to mark all the notifications as read. + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + seen: + type: string + enum: ["true", "false"] + description: '`true` to mark as read and `false` to mark as unread.' + example: "true" + required: + - seen security: - BaseTokenAuth: [] parameters: @@ -4580,9 +4584,12 @@ paths: type: object properties: seen: - type: boolean + type: string + enum: ["true", "false"] description: '`true` to mark as "seen" and `false` as "unseen".' - example: false + example: "true" + required: + - seen security: - BaseTokenAuth: [] parameters: diff --git a/tests/test_comments.py b/tests/test_comments.py index 5d36d6a..8e28d65 100644 --- a/tests/test_comments.py +++ b/tests/test_comments.py @@ -15,37 +15,12 @@ def _headers(base): def test_listRowComments(base: Base): - """Test listing comments for a row. - - Note: API returns [] (array) when no comments exist, but - {"comments": [...]} when comments exist. The schema says type:object - which is only true when comments exist. We test the empty case here - and accept both formats. - """ + """Test listing comments for a row.""" table_name = 'test_listRowComments' create_table(base, table_name, SIMPLE_COLUMNS) row_ids = append_rows(base, table_name, [{'text': 'target'}]) - import os, requests - server = os.environ['SEATABLE_SERVER'] - resp = requests.get( - f'{server}/api-gateway/api/v2/dtables/{base.uuid}/comments/', - params={'row_id': row_ids[0]}, - headers=_headers(base), - ) - - assert resp.status_code == 200 - data = resp.json() - # Empty: [] or {"comments": []} - assert isinstance(data, (list, dict)) - - -def test_getRowCommentsCount(base: Base): - table_name = 'test_getRowCommentsCount' - create_table(base, table_name, SIMPLE_COLUMNS) - row_ids = append_rows(base, table_name, [{'text': 'target'}]) - - case: Case = base_operations_schema.find_operation_by_id('getRowCommentsCount') \ + case: Case = base_operations_schema.find_operation_by_id('listRowComments') \ .Case( path_parameters={'base_uuid': base.uuid}, query={'row_id': row_ids[0]}, @@ -55,21 +30,26 @@ def test_getRowCommentsCount(base: Base): assert response.status_code == 200 data = response.json() + assert 'comment_list' in data assert 'count' in data -def test_listCommentsWithinDays(base: Base): - case: Case = base_operations_schema.find_operation_by_id('listCommentsWithinDays') \ +def test_getRowCommentsCount(base: Base): + table_name = 'test_getRowCommentsCount' + create_table(base, table_name, SIMPLE_COLUMNS) + row_ids = append_rows(base, table_name, [{'text': 'target'}]) + + case: Case = base_operations_schema.find_operation_by_id('getRowCommentsCount') \ .Case( path_parameters={'base_uuid': base.uuid}, - query={'days': 7}, + query={'row_id': row_ids[0]}, headers=_headers(base), ) response = case.call() assert response.status_code == 200 data = response.json() - assert 'comments' in data + assert 'count' in data def test_getNumberOfComments(base: Base): @@ -91,48 +71,11 @@ def _table_id(base: Base, table_name: str) -> str: def _list_comment_ids(base: Base, row_id: str) -> list[int]: - """createRowComment does not return the new comment's id, so look it up via listRowComments.""" + """createRowComment does not return the new comment's id, so list the row comments.""" case: Case = base_operations_schema.find_operation_by_id('listRowComments') \ .Case(path_parameters={'base_uuid': base.uuid}, query={'row_id': row_id}, headers=_headers(base)) data = case.call().json() - # API returns [] when no comments exist, {"comments": [...]} otherwise. - comments = data['comments'] if isinstance(data, dict) else data - return [c['id'] for c in comments] - - -def test_getComment(base: Base): - table_name = 'test_getComment' - create_table(base, table_name, SIMPLE_COLUMNS) - row_ids = append_rows(base, table_name, [{'text': 'comment target'}]) - - comment_text = 'Test comment from automated tests' - create: Case = base_operations_schema.find_operation_by_id('createRowComment') \ - .Case( - path_parameters={'base_uuid': base.uuid}, - query={'table_id': _table_id(base, table_name), 'row_id': row_ids[0]}, - body={'comment': comment_text}, - headers=_headers(base), - ) - create_response = create.call() - assert create_response.status_code == 200, \ - f'Failed to create comment: {create_response.status_code} {create_response.text}' - - # createRowComment does not return the comment ID, so we need to fetch all comments for this row - comment_ids = _list_comment_ids(base, row_ids[0]) - assert len(comment_ids) == 1 - comment_id = comment_ids[0] - - case: Case = base_operations_schema.find_operation_by_id('getComment') \ - .Case( - path_parameters={'base_uuid': base.uuid, 'comment_id': comment_id}, - headers=_headers(base), - ) - response = case.call() - - assert response.status_code == 200 - data = response.json() - assert data['id'] == comment_id - assert data['comment'] == comment_text + return [comment['id'] for comment in data['comment_list']] def test_deleteComment(base: Base): @@ -143,8 +86,11 @@ def test_deleteComment(base: Base): create: Case = base_operations_schema.find_operation_by_id('createRowComment') \ .Case( path_parameters={'base_uuid': base.uuid}, - query={'table_id': _table_id(base, table_name), 'row_id': row_ids[0]}, - body={'comment': 'Test comment from automated tests'}, + body={ + 'table_id': _table_id(base, table_name), + 'row_id': row_ids[0], + 'comment': 'Test comment from automated tests', + }, headers=_headers(base), ) create_response = create.call() diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 4863991..5773209 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -50,4 +50,5 @@ def test_listCollaborators(base: Base): user = data['user_list'][0] assert 'email' in user assert 'name' in user - assert 'contact_email' in user + assert 'id_in_org' in user + assert 'name_pinyin' in user diff --git a/tests/test_notifications.py b/tests/test_notifications.py index 36ff4c2..47636e3 100644 --- a/tests/test_notifications.py +++ b/tests/test_notifications.py @@ -22,12 +22,11 @@ def test_listBaseNotifications(base: Base): assert 'notification_list' in data -@pytest.mark.xfail(reason="API returns 400 'seen invalid' — expects form-encoded string 'true', not JSON boolean") def test_markBaseNotificationsAsSeen(base: Base): case: Case = base_operations_schema.find_operation_by_id('markBaseNotificationsAsSeen') \ .Case( path_parameters={'base_uuid': base.uuid}, - body={'seen': True}, + body={'seen': 'true'}, headers=_headers(base), ) response = case.call() @@ -46,18 +45,14 @@ def test_deleteBaseNotifications(base: Base): assert response.status_code == 200 -@pytest.mark.xfail(reason="API returns 400 'seen invalid' — expects form-encoded string 'true', not JSON boolean") def test_markBaseNotificationAsSeen(base: Base): """Mark a single notification as seen. Requires an existing notification_id.""" # First list notifications to get an ID - import os, requests - server = os.environ['SEATABLE_SERVER'] - resp = requests.get( - f'{server}/api-gateway/api/v2/dtables/{base.uuid}/notifications/', - headers=_headers(base), - ) - assert resp.status_code == 200 - notifications = resp.json().get('notification_list', []) + case: Case = base_operations_schema.find_operation_by_id('listBaseNotifications') \ + .Case(path_parameters={'base_uuid': base.uuid}, headers=_headers(base)) + response = case.call() + assert response.status_code == 200 + notifications = response.json().get('notification_list', []) if not notifications: pytest.skip('No notifications available to mark as seen') @@ -67,7 +62,7 @@ def test_markBaseNotificationAsSeen(base: Base): case: Case = base_operations_schema.find_operation_by_id('markBaseNotificationAsSeen') \ .Case( path_parameters={'base_uuid': base.uuid, 'notification_id': notification_id}, - body={'seen': True}, + body={'seen': 'true'}, headers=_headers(base), ) response = case.call()