mirror of
https://github.com/anyproto/anytype-heart.git
synced 2025-06-09 09:35:00 +09:00
GO-4459: Refactor into space, pagination, utils, remove chat
This commit is contained in:
parent
c379686478
commit
c0f69df4b9
15 changed files with 1235 additions and 2021 deletions
|
@ -200,7 +200,7 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "List of spaces",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.PaginatedResponse-api_Space"
|
||||
"$ref": "#/definitions/pagination.PaginatedResponse-space_Space"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
|
@ -249,7 +249,7 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "Space created successfully",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.CreateSpaceResponse"
|
||||
"$ref": "#/definitions/space.CreateSpaceResponse"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
|
@ -305,7 +305,7 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "List of members",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.PaginatedResponse-api_Member"
|
||||
"$ref": "#/definitions/pagination.PaginatedResponse-space_Member"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
|
@ -708,286 +708,9 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/spaces/{space_id}/chat/messages": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"chat"
|
||||
],
|
||||
"summary": "Retrieve last chat messages",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The ID of the space",
|
||||
"name": "space_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "The number of items to skip before starting to collect the result set",
|
||||
"name": "offset",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 100,
|
||||
"description": "The number of items to return",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of chat messages",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"502": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"chat"
|
||||
],
|
||||
"summary": "Add a new chat message",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The ID of the space",
|
||||
"name": "space_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Chat message",
|
||||
"name": "message",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created chat message",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ValidationError"
|
||||
}
|
||||
},
|
||||
"502": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/spaces/{space_id}/chat/messages/{message_id}": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"chat"
|
||||
],
|
||||
"summary": "Retrieve a specific chat message",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The ID of the space",
|
||||
"name": "space_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Message ID",
|
||||
"name": "message_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Chat message",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Message not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.NotFoundError"
|
||||
}
|
||||
},
|
||||
"502": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"chat"
|
||||
],
|
||||
"summary": "Update an existing chat message",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The ID of the space",
|
||||
"name": "space_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Message ID",
|
||||
"name": "message_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Chat message",
|
||||
"name": "message",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Updated chat message",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ValidationError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Message not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.NotFoundError"
|
||||
}
|
||||
},
|
||||
"502": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"chat"
|
||||
],
|
||||
"summary": "Delete a chat message",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The ID of the space",
|
||||
"name": "space_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Message ID",
|
||||
"name": "message_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Message deleted successfully"
|
||||
},
|
||||
"404": {
|
||||
"description": "Message not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.NotFoundError"
|
||||
}
|
||||
},
|
||||
"502": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.Attachment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"target": {
|
||||
"description": "Identifier for the attachment object",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "Type of attachment",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.AuthDisplayCodeResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1039,72 +762,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"api.ChatMessage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"attachments": {
|
||||
"description": "Attachments slice",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.Attachment"
|
||||
}
|
||||
},
|
||||
"chat_message": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "integer"
|
||||
},
|
||||
"creator": {
|
||||
"description": "Identifier for the message creator",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "Unique message identifier",
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"description": "Message content",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/api.MessageContent"
|
||||
}
|
||||
]
|
||||
},
|
||||
"modified_at": {
|
||||
"type": "integer"
|
||||
},
|
||||
"order_id": {
|
||||
"description": "Used for subscriptions",
|
||||
"type": "string"
|
||||
},
|
||||
"reactions": {
|
||||
"description": "Reactions to the message",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/api.Reactions"
|
||||
}
|
||||
]
|
||||
},
|
||||
"reply_to_message_id": {
|
||||
"description": "Identifier for the message being replied to",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.CreateSpaceResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "Space Name"
|
||||
},
|
||||
"space_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.Detail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1149,71 +806,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"api.IdentityList": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ids": {
|
||||
"description": "List of user IDs",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.Member": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"global_name": {
|
||||
"type": "string",
|
||||
"example": "john.any"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"example": "http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"example": "_participant_bafyreigyfkt6rbv24sbv5aq2hko1bhmv5xxlf22b4bypdu6j7hnphm3psq_23me69r569oi1_AAjEaEwPF4nkEh9AWkqEnzcQ8HziBB4ETjiTpvRCQvWnSMDZ"
|
||||
},
|
||||
"identity": {
|
||||
"type": "string",
|
||||
"example": "AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "John Doe"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"example": "Owner"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "member"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.MessageContent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"marks": {
|
||||
"description": "List of marks applied to the text",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"style": {
|
||||
"description": "The style/type of the message part",
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"description": "The text content of the message part",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.NotFoundError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1317,71 +909,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"api.PaginatedResponse-api_Member": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.Member"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"$ref": "#/definitions/api.PaginationMeta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.PaginatedResponse-api_Space": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.Space"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"$ref": "#/definitions/api.PaginationMeta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.PaginationMeta": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"has_more": {
|
||||
"description": "whether there are more items available",
|
||||
"type": "boolean",
|
||||
"example": true
|
||||
},
|
||||
"limit": {
|
||||
"description": "the current limit",
|
||||
"type": "integer",
|
||||
"example": 100
|
||||
},
|
||||
"offset": {
|
||||
"description": "the current offset",
|
||||
"type": "integer",
|
||||
"example": 0
|
||||
},
|
||||
"total": {
|
||||
"description": "the total number of items available on that endpoint",
|
||||
"type": "integer",
|
||||
"example": 1024
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.Reactions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reactions": {
|
||||
"description": "Map of emoji to list of user IDs",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/api.IdentityList"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.ServerError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1395,75 +922,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"api.Space": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"account_space_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreihpd2knon5wbljhtfeg3fcqtg3i2pomhhnigui6lrjmzcjzep7gcy.23me69r569oi1"
|
||||
},
|
||||
"archive_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreialsgoyflf3etjm3parzurivyaukzivwortf32b4twnlwpwocsrri"
|
||||
},
|
||||
"device_id": {
|
||||
"type": "string",
|
||||
"example": "12D3KooWGZMJ4kQVyQVXaj7gJPZr3RZ2nvd9M2Eq2pprEoPih9WF"
|
||||
},
|
||||
"home_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreie4qcl3wczb4cw5hrfyycikhjyh6oljdis3ewqrk5boaav3sbwqya"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"example": "http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"example": "bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"
|
||||
},
|
||||
"marketplace_workspace_id": {
|
||||
"type": "string",
|
||||
"example": "_anytype_marketplace"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "Space Name"
|
||||
},
|
||||
"network_id": {
|
||||
"type": "string",
|
||||
"example": "N83gJpVd9MuNRZAuJLZ7LiMntTThhPc6DtzWWVjb1M3PouVU"
|
||||
},
|
||||
"profile_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreiaxhwreshjqwndpwtdsu4mtihaqhhmlygqnyqpfyfwlqfq3rm3gw4"
|
||||
},
|
||||
"space_view_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreigzv3vq7qwlrsin6njoduq727ssnhwd6bgyfj6nm4hv3pxoc2rxhy"
|
||||
},
|
||||
"tech_space_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreif4xuwncrjl6jajt4zrrfnylpki476nv2w64yf42ovt7gia7oypii.23me69r569oi1"
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"example": ""
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "space"
|
||||
},
|
||||
"widgets_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreialj7pceh53mifm5dixlho47ke4qjmsn2uh4wsjf7xq2pnlo5xfva"
|
||||
},
|
||||
"workspace_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreiapey2g6e6za4zfxvlgwdy4hbbfu676gmwrhnqvjbxvrchr7elr3y"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.Text": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1509,6 +967,181 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"pagination.PaginatedResponse-space_Member": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/space.Member"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"$ref": "#/definitions/pagination.PaginationMeta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pagination.PaginatedResponse-space_Space": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/space.Space"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"$ref": "#/definitions/pagination.PaginationMeta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pagination.PaginationMeta": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"has_more": {
|
||||
"description": "whether there are more items available",
|
||||
"type": "boolean",
|
||||
"example": true
|
||||
},
|
||||
"limit": {
|
||||
"description": "the current limit",
|
||||
"type": "integer",
|
||||
"example": 100
|
||||
},
|
||||
"offset": {
|
||||
"description": "the current offset",
|
||||
"type": "integer",
|
||||
"example": 0
|
||||
},
|
||||
"total": {
|
||||
"description": "the total number of items available on that endpoint",
|
||||
"type": "integer",
|
||||
"example": 1024
|
||||
}
|
||||
}
|
||||
},
|
||||
"space.CreateSpaceResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"space": {
|
||||
"$ref": "#/definitions/space.Space"
|
||||
}
|
||||
}
|
||||
},
|
||||
"space.Member": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"global_name": {
|
||||
"type": "string",
|
||||
"example": "john.any"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"example": "http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"example": "_participant_bafyreigyfkt6rbv24sbv5aq2hko1bhmv5xxlf22b4bypdu6j7hnphm3psq_23me69r569oi1_AAjEaEwPF4nkEh9AWkqEnzcQ8HziBB4ETjiTpvRCQvWnSMDZ"
|
||||
},
|
||||
"identity": {
|
||||
"type": "string",
|
||||
"example": "AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "John Doe"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"example": "Owner"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "member"
|
||||
}
|
||||
}
|
||||
},
|
||||
"space.Space": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"account_space_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreihpd2knon5wbljhtfeg3fcqtg3i2pomhhnigui6lrjmzcjzep7gcy.23me69r569oi1"
|
||||
},
|
||||
"analytics_id": {
|
||||
"type": "string",
|
||||
"example": ""
|
||||
},
|
||||
"archive_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreialsgoyflf3etjm3parzurivyaukzivwortf32b4twnlwpwocsrri"
|
||||
},
|
||||
"device_id": {
|
||||
"type": "string",
|
||||
"example": "12D3KooWGZMJ4kQVyQVXaj7gJPZr3RZ2nvd9M2Eq2pprEoPih9WF"
|
||||
},
|
||||
"gateway_url": {
|
||||
"type": "string",
|
||||
"example": ""
|
||||
},
|
||||
"home_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreie4qcl3wczb4cw5hrfyycikhjyh6oljdis3ewqrk5boaav3sbwqya"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"example": "http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"example": "bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"
|
||||
},
|
||||
"local_storage_path": {
|
||||
"type": "string",
|
||||
"example": ""
|
||||
},
|
||||
"marketplace_workspace_id": {
|
||||
"type": "string",
|
||||
"example": "_anytype_marketplace"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "Space Name"
|
||||
},
|
||||
"network_id": {
|
||||
"type": "string",
|
||||
"example": "N83gJpVd9MuNRZAuJLZ7LiMntTThhPc6DtzWWVjb1M3PouVU"
|
||||
},
|
||||
"profile_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreiaxhwreshjqwndpwtdsu4mtihaqhhmlygqnyqpfyfwlqfq3rm3gw4"
|
||||
},
|
||||
"space_view_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreigzv3vq7qwlrsin6njoduq727ssnhwd6bgyfj6nm4hv3pxoc2rxhy"
|
||||
},
|
||||
"tech_space_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreif4xuwncrjl6jajt4zrrfnylpki476nv2w64yf42ovt7gia7oypii.23me69r569oi1"
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"example": ""
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "space"
|
||||
},
|
||||
"widgets_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreialj7pceh53mifm5dixlho47ke4qjmsn2uh4wsjf7xq2pnlo5xfva"
|
||||
},
|
||||
"workspace_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreiapey2g6e6za4zfxvlgwdy4hbbfu676gmwrhnqvjbxvrchr7elr3y"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
|
|
@ -194,7 +194,7 @@
|
|||
"200": {
|
||||
"description": "List of spaces",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.PaginatedResponse-api_Space"
|
||||
"$ref": "#/definitions/pagination.PaginatedResponse-space_Space"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
|
@ -243,7 +243,7 @@
|
|||
"200": {
|
||||
"description": "Space created successfully",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.CreateSpaceResponse"
|
||||
"$ref": "#/definitions/space.CreateSpaceResponse"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
|
@ -299,7 +299,7 @@
|
|||
"200": {
|
||||
"description": "List of members",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.PaginatedResponse-api_Member"
|
||||
"$ref": "#/definitions/pagination.PaginatedResponse-space_Member"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
|
@ -702,286 +702,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/spaces/{space_id}/chat/messages": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"chat"
|
||||
],
|
||||
"summary": "Retrieve last chat messages",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The ID of the space",
|
||||
"name": "space_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "The number of items to skip before starting to collect the result set",
|
||||
"name": "offset",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 100,
|
||||
"description": "The number of items to return",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of chat messages",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"502": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"chat"
|
||||
],
|
||||
"summary": "Add a new chat message",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The ID of the space",
|
||||
"name": "space_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Chat message",
|
||||
"name": "message",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created chat message",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ValidationError"
|
||||
}
|
||||
},
|
||||
"502": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/spaces/{space_id}/chat/messages/{message_id}": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"chat"
|
||||
],
|
||||
"summary": "Retrieve a specific chat message",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The ID of the space",
|
||||
"name": "space_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Message ID",
|
||||
"name": "message_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Chat message",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Message not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.NotFoundError"
|
||||
}
|
||||
},
|
||||
"502": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"chat"
|
||||
],
|
||||
"summary": "Update an existing chat message",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The ID of the space",
|
||||
"name": "space_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Message ID",
|
||||
"name": "message_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Chat message",
|
||||
"name": "message",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Updated chat message",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ChatMessage"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ValidationError"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Message not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.NotFoundError"
|
||||
}
|
||||
},
|
||||
"502": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"chat"
|
||||
],
|
||||
"summary": "Delete a chat message",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The ID of the space",
|
||||
"name": "space_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Message ID",
|
||||
"name": "message_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Message deleted successfully"
|
||||
},
|
||||
"404": {
|
||||
"description": "Message not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.NotFoundError"
|
||||
}
|
||||
},
|
||||
"502": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.Attachment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"target": {
|
||||
"description": "Identifier for the attachment object",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "Type of attachment",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.AuthDisplayCodeResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1033,72 +756,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"api.ChatMessage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"attachments": {
|
||||
"description": "Attachments slice",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.Attachment"
|
||||
}
|
||||
},
|
||||
"chat_message": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "integer"
|
||||
},
|
||||
"creator": {
|
||||
"description": "Identifier for the message creator",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "Unique message identifier",
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"description": "Message content",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/api.MessageContent"
|
||||
}
|
||||
]
|
||||
},
|
||||
"modified_at": {
|
||||
"type": "integer"
|
||||
},
|
||||
"order_id": {
|
||||
"description": "Used for subscriptions",
|
||||
"type": "string"
|
||||
},
|
||||
"reactions": {
|
||||
"description": "Reactions to the message",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/api.Reactions"
|
||||
}
|
||||
]
|
||||
},
|
||||
"reply_to_message_id": {
|
||||
"description": "Identifier for the message being replied to",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.CreateSpaceResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "Space Name"
|
||||
},
|
||||
"space_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.Detail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1143,71 +800,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"api.IdentityList": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ids": {
|
||||
"description": "List of user IDs",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.Member": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"global_name": {
|
||||
"type": "string",
|
||||
"example": "john.any"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"example": "http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"example": "_participant_bafyreigyfkt6rbv24sbv5aq2hko1bhmv5xxlf22b4bypdu6j7hnphm3psq_23me69r569oi1_AAjEaEwPF4nkEh9AWkqEnzcQ8HziBB4ETjiTpvRCQvWnSMDZ"
|
||||
},
|
||||
"identity": {
|
||||
"type": "string",
|
||||
"example": "AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "John Doe"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"example": "Owner"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "member"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.MessageContent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"marks": {
|
||||
"description": "List of marks applied to the text",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"style": {
|
||||
"description": "The style/type of the message part",
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"description": "The text content of the message part",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.NotFoundError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1311,71 +903,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"api.PaginatedResponse-api_Member": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.Member"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"$ref": "#/definitions/api.PaginationMeta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.PaginatedResponse-api_Space": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.Space"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"$ref": "#/definitions/api.PaginationMeta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.PaginationMeta": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"has_more": {
|
||||
"description": "whether there are more items available",
|
||||
"type": "boolean",
|
||||
"example": true
|
||||
},
|
||||
"limit": {
|
||||
"description": "the current limit",
|
||||
"type": "integer",
|
||||
"example": 100
|
||||
},
|
||||
"offset": {
|
||||
"description": "the current offset",
|
||||
"type": "integer",
|
||||
"example": 0
|
||||
},
|
||||
"total": {
|
||||
"description": "the total number of items available on that endpoint",
|
||||
"type": "integer",
|
||||
"example": 1024
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.Reactions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reactions": {
|
||||
"description": "Map of emoji to list of user IDs",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/api.IdentityList"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.ServerError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1389,75 +916,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"api.Space": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"account_space_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreihpd2knon5wbljhtfeg3fcqtg3i2pomhhnigui6lrjmzcjzep7gcy.23me69r569oi1"
|
||||
},
|
||||
"archive_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreialsgoyflf3etjm3parzurivyaukzivwortf32b4twnlwpwocsrri"
|
||||
},
|
||||
"device_id": {
|
||||
"type": "string",
|
||||
"example": "12D3KooWGZMJ4kQVyQVXaj7gJPZr3RZ2nvd9M2Eq2pprEoPih9WF"
|
||||
},
|
||||
"home_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreie4qcl3wczb4cw5hrfyycikhjyh6oljdis3ewqrk5boaav3sbwqya"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"example": "http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"example": "bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"
|
||||
},
|
||||
"marketplace_workspace_id": {
|
||||
"type": "string",
|
||||
"example": "_anytype_marketplace"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "Space Name"
|
||||
},
|
||||
"network_id": {
|
||||
"type": "string",
|
||||
"example": "N83gJpVd9MuNRZAuJLZ7LiMntTThhPc6DtzWWVjb1M3PouVU"
|
||||
},
|
||||
"profile_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreiaxhwreshjqwndpwtdsu4mtihaqhhmlygqnyqpfyfwlqfq3rm3gw4"
|
||||
},
|
||||
"space_view_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreigzv3vq7qwlrsin6njoduq727ssnhwd6bgyfj6nm4hv3pxoc2rxhy"
|
||||
},
|
||||
"tech_space_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreif4xuwncrjl6jajt4zrrfnylpki476nv2w64yf42ovt7gia7oypii.23me69r569oi1"
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"example": ""
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "space"
|
||||
},
|
||||
"widgets_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreialj7pceh53mifm5dixlho47ke4qjmsn2uh4wsjf7xq2pnlo5xfva"
|
||||
},
|
||||
"workspace_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreiapey2g6e6za4zfxvlgwdy4hbbfu676gmwrhnqvjbxvrchr7elr3y"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.Text": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1503,6 +961,181 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"pagination.PaginatedResponse-space_Member": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/space.Member"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"$ref": "#/definitions/pagination.PaginationMeta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pagination.PaginatedResponse-space_Space": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/space.Space"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"$ref": "#/definitions/pagination.PaginationMeta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pagination.PaginationMeta": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"has_more": {
|
||||
"description": "whether there are more items available",
|
||||
"type": "boolean",
|
||||
"example": true
|
||||
},
|
||||
"limit": {
|
||||
"description": "the current limit",
|
||||
"type": "integer",
|
||||
"example": 100
|
||||
},
|
||||
"offset": {
|
||||
"description": "the current offset",
|
||||
"type": "integer",
|
||||
"example": 0
|
||||
},
|
||||
"total": {
|
||||
"description": "the total number of items available on that endpoint",
|
||||
"type": "integer",
|
||||
"example": 1024
|
||||
}
|
||||
}
|
||||
},
|
||||
"space.CreateSpaceResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"space": {
|
||||
"$ref": "#/definitions/space.Space"
|
||||
}
|
||||
}
|
||||
},
|
||||
"space.Member": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"global_name": {
|
||||
"type": "string",
|
||||
"example": "john.any"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"example": "http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"example": "_participant_bafyreigyfkt6rbv24sbv5aq2hko1bhmv5xxlf22b4bypdu6j7hnphm3psq_23me69r569oi1_AAjEaEwPF4nkEh9AWkqEnzcQ8HziBB4ETjiTpvRCQvWnSMDZ"
|
||||
},
|
||||
"identity": {
|
||||
"type": "string",
|
||||
"example": "AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "John Doe"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"example": "Owner"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "member"
|
||||
}
|
||||
}
|
||||
},
|
||||
"space.Space": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"account_space_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreihpd2knon5wbljhtfeg3fcqtg3i2pomhhnigui6lrjmzcjzep7gcy.23me69r569oi1"
|
||||
},
|
||||
"analytics_id": {
|
||||
"type": "string",
|
||||
"example": ""
|
||||
},
|
||||
"archive_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreialsgoyflf3etjm3parzurivyaukzivwortf32b4twnlwpwocsrri"
|
||||
},
|
||||
"device_id": {
|
||||
"type": "string",
|
||||
"example": "12D3KooWGZMJ4kQVyQVXaj7gJPZr3RZ2nvd9M2Eq2pprEoPih9WF"
|
||||
},
|
||||
"gateway_url": {
|
||||
"type": "string",
|
||||
"example": ""
|
||||
},
|
||||
"home_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreie4qcl3wczb4cw5hrfyycikhjyh6oljdis3ewqrk5boaav3sbwqya"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"example": "http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"example": "bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"
|
||||
},
|
||||
"local_storage_path": {
|
||||
"type": "string",
|
||||
"example": ""
|
||||
},
|
||||
"marketplace_workspace_id": {
|
||||
"type": "string",
|
||||
"example": "_anytype_marketplace"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "Space Name"
|
||||
},
|
||||
"network_id": {
|
||||
"type": "string",
|
||||
"example": "N83gJpVd9MuNRZAuJLZ7LiMntTThhPc6DtzWWVjb1M3PouVU"
|
||||
},
|
||||
"profile_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreiaxhwreshjqwndpwtdsu4mtihaqhhmlygqnyqpfyfwlqfq3rm3gw4"
|
||||
},
|
||||
"space_view_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreigzv3vq7qwlrsin6njoduq727ssnhwd6bgyfj6nm4hv3pxoc2rxhy"
|
||||
},
|
||||
"tech_space_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreif4xuwncrjl6jajt4zrrfnylpki476nv2w64yf42ovt7gia7oypii.23me69r569oi1"
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"example": ""
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "space"
|
||||
},
|
||||
"widgets_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreialj7pceh53mifm5dixlho47ke4qjmsn2uh4wsjf7xq2pnlo5xfva"
|
||||
},
|
||||
"workspace_object_id": {
|
||||
"type": "string",
|
||||
"example": "bafyreiapey2g6e6za4zfxvlgwdy4hbbfu676gmwrhnqvjbxvrchr7elr3y"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
basePath: /v1
|
||||
definitions:
|
||||
api.Attachment:
|
||||
properties:
|
||||
target:
|
||||
description: Identifier for the attachment object
|
||||
type: string
|
||||
type:
|
||||
description: Type of attachment
|
||||
type: string
|
||||
type: object
|
||||
api.AuthDisplayCodeResponse:
|
||||
properties:
|
||||
challenge_id:
|
||||
|
@ -43,49 +34,6 @@ definitions:
|
|||
vertical_align:
|
||||
type: string
|
||||
type: object
|
||||
api.ChatMessage:
|
||||
properties:
|
||||
attachments:
|
||||
description: Attachments slice
|
||||
items:
|
||||
$ref: '#/definitions/api.Attachment'
|
||||
type: array
|
||||
chat_message:
|
||||
type: string
|
||||
created_at:
|
||||
type: integer
|
||||
creator:
|
||||
description: Identifier for the message creator
|
||||
type: string
|
||||
id:
|
||||
description: Unique message identifier
|
||||
type: string
|
||||
message:
|
||||
allOf:
|
||||
- $ref: '#/definitions/api.MessageContent'
|
||||
description: Message content
|
||||
modified_at:
|
||||
type: integer
|
||||
order_id:
|
||||
description: Used for subscriptions
|
||||
type: string
|
||||
reactions:
|
||||
allOf:
|
||||
- $ref: '#/definitions/api.Reactions'
|
||||
description: Reactions to the message
|
||||
reply_to_message_id:
|
||||
description: Identifier for the message being replied to
|
||||
type: string
|
||||
type: object
|
||||
api.CreateSpaceResponse:
|
||||
properties:
|
||||
name:
|
||||
example: Space Name
|
||||
type: string
|
||||
space_id:
|
||||
example: bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1
|
||||
type: string
|
||||
type: object
|
||||
api.Detail:
|
||||
properties:
|
||||
details:
|
||||
|
@ -115,52 +63,6 @@ definitions:
|
|||
type:
|
||||
type: string
|
||||
type: object
|
||||
api.IdentityList:
|
||||
properties:
|
||||
ids:
|
||||
description: List of user IDs
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
api.Member:
|
||||
properties:
|
||||
global_name:
|
||||
example: john.any
|
||||
type: string
|
||||
icon:
|
||||
example: http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100
|
||||
type: string
|
||||
id:
|
||||
example: _participant_bafyreigyfkt6rbv24sbv5aq2hko1bhmv5xxlf22b4bypdu6j7hnphm3psq_23me69r569oi1_AAjEaEwPF4nkEh9AWkqEnzcQ8HziBB4ETjiTpvRCQvWnSMDZ
|
||||
type: string
|
||||
identity:
|
||||
example: AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ
|
||||
type: string
|
||||
name:
|
||||
example: John Doe
|
||||
type: string
|
||||
role:
|
||||
example: Owner
|
||||
type: string
|
||||
type:
|
||||
example: member
|
||||
type: string
|
||||
type: object
|
||||
api.MessageContent:
|
||||
properties:
|
||||
marks:
|
||||
description: List of marks applied to the text
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
style:
|
||||
description: The style/type of the message part
|
||||
type: string
|
||||
text:
|
||||
description: The text content of the message part
|
||||
type: string
|
||||
type: object
|
||||
api.NotFoundError:
|
||||
properties:
|
||||
error:
|
||||
|
@ -233,25 +135,62 @@ definitions:
|
|||
example: ot-page
|
||||
type: string
|
||||
type: object
|
||||
api.PaginatedResponse-api_Member:
|
||||
api.ServerError:
|
||||
properties:
|
||||
error:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
api.Text:
|
||||
properties:
|
||||
checked:
|
||||
type: boolean
|
||||
color:
|
||||
type: string
|
||||
icon:
|
||||
type: string
|
||||
style:
|
||||
type: string
|
||||
text:
|
||||
type: string
|
||||
type: object
|
||||
api.UnauthorizedError:
|
||||
properties:
|
||||
error:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
api.ValidationError:
|
||||
properties:
|
||||
error:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
pagination.PaginatedResponse-space_Member:
|
||||
properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/api.Member'
|
||||
$ref: '#/definitions/space.Member'
|
||||
type: array
|
||||
pagination:
|
||||
$ref: '#/definitions/api.PaginationMeta'
|
||||
$ref: '#/definitions/pagination.PaginationMeta'
|
||||
type: object
|
||||
api.PaginatedResponse-api_Space:
|
||||
pagination.PaginatedResponse-space_Space:
|
||||
properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/api.Space'
|
||||
$ref: '#/definitions/space.Space'
|
||||
type: array
|
||||
pagination:
|
||||
$ref: '#/definitions/api.PaginationMeta'
|
||||
$ref: '#/definitions/pagination.PaginationMeta'
|
||||
type: object
|
||||
api.PaginationMeta:
|
||||
pagination.PaginationMeta:
|
||||
properties:
|
||||
has_more:
|
||||
description: whether there are more items available
|
||||
|
@ -270,33 +209,52 @@ definitions:
|
|||
example: 1024
|
||||
type: integer
|
||||
type: object
|
||||
api.Reactions:
|
||||
space.CreateSpaceResponse:
|
||||
properties:
|
||||
reactions:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/api.IdentityList'
|
||||
description: Map of emoji to list of user IDs
|
||||
type: object
|
||||
space:
|
||||
$ref: '#/definitions/space.Space'
|
||||
type: object
|
||||
api.ServerError:
|
||||
space.Member:
|
||||
properties:
|
||||
error:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
global_name:
|
||||
example: john.any
|
||||
type: string
|
||||
icon:
|
||||
example: http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100
|
||||
type: string
|
||||
id:
|
||||
example: _participant_bafyreigyfkt6rbv24sbv5aq2hko1bhmv5xxlf22b4bypdu6j7hnphm3psq_23me69r569oi1_AAjEaEwPF4nkEh9AWkqEnzcQ8HziBB4ETjiTpvRCQvWnSMDZ
|
||||
type: string
|
||||
identity:
|
||||
example: AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ
|
||||
type: string
|
||||
name:
|
||||
example: John Doe
|
||||
type: string
|
||||
role:
|
||||
example: Owner
|
||||
type: string
|
||||
type:
|
||||
example: member
|
||||
type: string
|
||||
type: object
|
||||
api.Space:
|
||||
space.Space:
|
||||
properties:
|
||||
account_space_id:
|
||||
example: bafyreihpd2knon5wbljhtfeg3fcqtg3i2pomhhnigui6lrjmzcjzep7gcy.23me69r569oi1
|
||||
type: string
|
||||
analytics_id:
|
||||
example: ""
|
||||
type: string
|
||||
archive_object_id:
|
||||
example: bafyreialsgoyflf3etjm3parzurivyaukzivwortf32b4twnlwpwocsrri
|
||||
type: string
|
||||
device_id:
|
||||
example: 12D3KooWGZMJ4kQVyQVXaj7gJPZr3RZ2nvd9M2Eq2pprEoPih9WF
|
||||
type: string
|
||||
gateway_url:
|
||||
example: ""
|
||||
type: string
|
||||
home_object_id:
|
||||
example: bafyreie4qcl3wczb4cw5hrfyycikhjyh6oljdis3ewqrk5boaav3sbwqya
|
||||
type: string
|
||||
|
@ -306,6 +264,9 @@ definitions:
|
|||
id:
|
||||
example: bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1
|
||||
type: string
|
||||
local_storage_path:
|
||||
example: ""
|
||||
type: string
|
||||
marketplace_workspace_id:
|
||||
example: _anytype_marketplace
|
||||
type: string
|
||||
|
@ -337,35 +298,6 @@ definitions:
|
|||
example: bafyreiapey2g6e6za4zfxvlgwdy4hbbfu676gmwrhnqvjbxvrchr7elr3y
|
||||
type: string
|
||||
type: object
|
||||
api.Text:
|
||||
properties:
|
||||
checked:
|
||||
type: boolean
|
||||
color:
|
||||
type: string
|
||||
icon:
|
||||
type: string
|
||||
style:
|
||||
type: string
|
||||
text:
|
||||
type: string
|
||||
type: object
|
||||
api.UnauthorizedError:
|
||||
properties:
|
||||
error:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
api.ValidationError:
|
||||
properties:
|
||||
error:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
externalDocs:
|
||||
description: OpenAPI
|
||||
url: https://swagger.io/resources/open-api/
|
||||
|
@ -501,7 +433,7 @@ paths:
|
|||
"200":
|
||||
description: List of spaces
|
||||
schema:
|
||||
$ref: '#/definitions/api.PaginatedResponse-api_Space'
|
||||
$ref: '#/definitions/pagination.PaginatedResponse-space_Space'
|
||||
"403":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
|
@ -533,7 +465,7 @@ paths:
|
|||
"200":
|
||||
description: Space created successfully
|
||||
schema:
|
||||
$ref: '#/definitions/api.CreateSpaceResponse'
|
||||
$ref: '#/definitions/space.CreateSpaceResponse'
|
||||
"403":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
|
@ -571,7 +503,7 @@ paths:
|
|||
"200":
|
||||
description: List of members
|
||||
schema:
|
||||
$ref: '#/definitions/api.PaginatedResponse-api_Member'
|
||||
$ref: '#/definitions/pagination.PaginatedResponse-space_Member'
|
||||
"403":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
|
@ -842,182 +774,6 @@ paths:
|
|||
summary: Update an existing object in a specific space
|
||||
tags:
|
||||
- space_objects
|
||||
/v1/spaces/{space_id}/chat/messages:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: The ID of the space
|
||||
in: path
|
||||
name: space_id
|
||||
required: true
|
||||
type: string
|
||||
- description: The number of items to skip before starting to collect the result
|
||||
set
|
||||
in: query
|
||||
name: offset
|
||||
type: integer
|
||||
- default: 100
|
||||
description: The number of items to return
|
||||
in: query
|
||||
name: limit
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: List of chat messages
|
||||
schema:
|
||||
additionalProperties:
|
||||
items:
|
||||
$ref: '#/definitions/api.ChatMessage'
|
||||
type: array
|
||||
type: object
|
||||
"502":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ServerError'
|
||||
summary: Retrieve last chat messages
|
||||
tags:
|
||||
- chat
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: The ID of the space
|
||||
in: path
|
||||
name: space_id
|
||||
required: true
|
||||
type: string
|
||||
- description: Chat message
|
||||
in: body
|
||||
name: message
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/api.ChatMessage'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created chat message
|
||||
schema:
|
||||
$ref: '#/definitions/api.ChatMessage'
|
||||
"400":
|
||||
description: Invalid input
|
||||
schema:
|
||||
$ref: '#/definitions/api.ValidationError'
|
||||
"502":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ServerError'
|
||||
summary: Add a new chat message
|
||||
tags:
|
||||
- chat
|
||||
/v1/spaces/{space_id}/chat/messages/{message_id}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: The ID of the space
|
||||
in: path
|
||||
name: space_id
|
||||
required: true
|
||||
type: string
|
||||
- description: Message ID
|
||||
in: path
|
||||
name: message_id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: Message deleted successfully
|
||||
"404":
|
||||
description: Message not found
|
||||
schema:
|
||||
$ref: '#/definitions/api.NotFoundError'
|
||||
"502":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ServerError'
|
||||
summary: Delete a chat message
|
||||
tags:
|
||||
- chat
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: The ID of the space
|
||||
in: path
|
||||
name: space_id
|
||||
required: true
|
||||
type: string
|
||||
- description: Message ID
|
||||
in: path
|
||||
name: message_id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Chat message
|
||||
schema:
|
||||
$ref: '#/definitions/api.ChatMessage'
|
||||
"404":
|
||||
description: Message not found
|
||||
schema:
|
||||
$ref: '#/definitions/api.NotFoundError'
|
||||
"502":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ServerError'
|
||||
summary: Retrieve a specific chat message
|
||||
tags:
|
||||
- chat
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: The ID of the space
|
||||
in: path
|
||||
name: space_id
|
||||
required: true
|
||||
type: string
|
||||
- description: Message ID
|
||||
in: path
|
||||
name: message_id
|
||||
required: true
|
||||
type: string
|
||||
- description: Chat message
|
||||
in: body
|
||||
name: message
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/api.ChatMessage'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Updated chat message
|
||||
schema:
|
||||
$ref: '#/definitions/api.ChatMessage'
|
||||
"400":
|
||||
description: Invalid input
|
||||
schema:
|
||||
$ref: '#/definitions/api.ValidationError'
|
||||
"404":
|
||||
description: Message not found
|
||||
schema:
|
||||
$ref: '#/definitions/api.NotFoundError'
|
||||
"502":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ServerError'
|
||||
summary: Update an existing chat message
|
||||
tags:
|
||||
- chat
|
||||
securityDefinitions:
|
||||
BasicAuth:
|
||||
type: basic
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gogo/protobuf/types"
|
||||
|
||||
"github.com/anyproto/anytype-heart/cmd/api/pagination"
|
||||
"github.com/anyproto/anytype-heart/cmd/api/utils"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
)
|
||||
|
||||
type CreateSpaceRequest struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type CreateObjectRequest struct {
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
|
@ -86,205 +82,6 @@ func (a *ApiServer) authTokenHandler(c *gin.Context) {
|
|||
})
|
||||
}
|
||||
|
||||
// getSpacesHandler retrieves a list of spaces
|
||||
//
|
||||
// @Summary Retrieve a list of spaces
|
||||
// @Tags spaces
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set"
|
||||
// @Param limit query int false "The number of items to return" default(100)
|
||||
// @Success 200 {object} PaginatedResponse[Space] "List of spaces"
|
||||
// @Failure 403 {object} UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} NotFoundError "Resource not found"
|
||||
// @Failure 502 {object} ServerError "Internal server error"
|
||||
// @Router /spaces [get]
|
||||
func (a *ApiServer) getSpacesHandler(c *gin.Context) {
|
||||
offset := c.GetInt("offset")
|
||||
limit := c.GetInt("limit")
|
||||
|
||||
// Call ObjectSearch for all objects of type spaceView
|
||||
resp := a.mw.ObjectSearch(c.Request.Context(), &pb.RpcObjectSearchRequest{
|
||||
SpaceId: a.accountInfo.TechSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_spaceView)),
|
||||
},
|
||||
{
|
||||
RelationKey: bundle.RelationKeySpaceLocalStatus.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.SpaceStatus_Ok)),
|
||||
},
|
||||
{
|
||||
RelationKey: bundle.RelationKeySpaceRemoteStatus.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.SpaceStatus_Ok)),
|
||||
},
|
||||
},
|
||||
Sorts: []*model.BlockContentDataviewSort{
|
||||
{
|
||||
RelationKey: "spaceOrder",
|
||||
Type: model.BlockContentDataviewSort_Asc,
|
||||
NoCollate: true,
|
||||
EmptyPlacement: model.BlockContentDataviewSort_End,
|
||||
},
|
||||
},
|
||||
Keys: []string{"targetSpaceId", "name", "iconEmoji", "iconImage"},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to retrieve list of spaces."})
|
||||
return
|
||||
}
|
||||
|
||||
if len(resp.Records) == 0 {
|
||||
c.JSON(http.StatusNotFound, gin.H{"message": "No spaces found."})
|
||||
return
|
||||
}
|
||||
|
||||
paginatedSpaces, hasMore := paginate(resp.Records, offset, limit)
|
||||
spaces := make([]Space, 0, len(paginatedSpaces))
|
||||
|
||||
for _, record := range paginatedSpaces {
|
||||
workspace, statusCode, errorMessage := a.getWorkspaceInfo(record.Fields["targetSpaceId"].GetStringValue())
|
||||
if statusCode != http.StatusOK {
|
||||
c.JSON(statusCode, gin.H{"message": errorMessage})
|
||||
return
|
||||
}
|
||||
|
||||
workspace.Name = record.Fields["name"].GetStringValue()
|
||||
workspace.Icon = a.getIconFromEmojiOrImage(record.Fields["iconEmoji"].GetStringValue(), record.Fields["iconImage"].GetStringValue())
|
||||
|
||||
spaces = append(spaces, workspace)
|
||||
}
|
||||
|
||||
respondWithPagination(c, http.StatusOK, spaces, len(resp.Records), offset, limit, hasMore)
|
||||
}
|
||||
|
||||
// createSpaceHandler creates a new space
|
||||
//
|
||||
// @Summary Create a new Space
|
||||
// @Tags spaces
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param name body string true "Space Name"
|
||||
// @Success 200 {object} CreateSpaceResponse "Space created successfully"
|
||||
// @Failure 403 {object} UnauthorizedError "Unauthorized"
|
||||
// @Failure 502 {object} ServerError "Internal server error"
|
||||
// @Router /spaces [post]
|
||||
func (a *ApiServer) createSpaceHandler(c *gin.Context) {
|
||||
nameRequest := CreateSpaceRequest{}
|
||||
if err := c.BindJSON(&nameRequest); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"message": "Invalid JSON"})
|
||||
return
|
||||
}
|
||||
name := nameRequest.Name
|
||||
iconOption, err := rand.Int(rand.Reader, big.NewInt(13))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to generate random icon."})
|
||||
return
|
||||
}
|
||||
|
||||
// Create new workspace with a random icon and import default use case
|
||||
resp := a.mw.WorkspaceCreate(c.Request.Context(), &pb.RpcWorkspaceCreateRequest{
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
"iconOption": {Kind: &types.Value_NumberValue{NumberValue: float64(iconOption.Int64())}},
|
||||
"name": {Kind: &types.Value_StringValue{StringValue: name}},
|
||||
"spaceDashboardId": {Kind: &types.Value_StringValue{
|
||||
StringValue: "lastOpened",
|
||||
}},
|
||||
},
|
||||
},
|
||||
UseCase: pb.RpcObjectImportUseCaseRequest_GET_STARTED,
|
||||
WithChat: true,
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcWorkspaceCreateResponseError_NULL {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to create a new space."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, CreateSpaceResponse{SpaceId: resp.SpaceId, Name: name})
|
||||
}
|
||||
|
||||
// getMembersHandler retrieves a list of members for the specified space
|
||||
//
|
||||
// @Summary Retrieve a list of members for the specified Space
|
||||
// @Tags spaces
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "The ID of the space"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set"
|
||||
// @Param limit query int false "The number of items to return" default(100)
|
||||
// @Success 200 {object} PaginatedResponse[Member] "List of members"
|
||||
// @Failure 403 {object} UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} NotFoundError "Resource not found"
|
||||
// @Failure 502 {object} ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/members [get]
|
||||
func (a *ApiServer) getMembersHandler(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
offset := c.GetInt("offset")
|
||||
limit := c.GetInt("limit")
|
||||
|
||||
// Call ObjectSearch for all objects of type participant
|
||||
resp := a.mw.ObjectSearch(c.Request.Context(), &pb.RpcObjectSearchRequest{
|
||||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_participant)),
|
||||
},
|
||||
{
|
||||
RelationKey: bundle.RelationKeyParticipantStatus.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ParticipantStatus_Active)),
|
||||
},
|
||||
},
|
||||
Sorts: []*model.BlockContentDataviewSort{
|
||||
{
|
||||
RelationKey: "name",
|
||||
Type: model.BlockContentDataviewSort_Asc,
|
||||
},
|
||||
},
|
||||
Keys: []string{"id", "name", "iconEmoji", "iconImage", "identity", "globalName", "participantPermissions"},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to retrieve list of members."})
|
||||
return
|
||||
}
|
||||
|
||||
if len(resp.Records) == 0 {
|
||||
c.JSON(http.StatusNotFound, gin.H{"message": "No members found."})
|
||||
return
|
||||
}
|
||||
|
||||
paginatedMembers, hasMore := paginate(resp.Records, offset, limit)
|
||||
members := make([]Member, 0, len(paginatedMembers))
|
||||
|
||||
for _, record := range paginatedMembers {
|
||||
icon := a.getIconFromEmojiOrImage(record.Fields["iconEmoji"].GetStringValue(), record.Fields["iconImage"].GetStringValue())
|
||||
|
||||
member := Member{
|
||||
Type: "space_member",
|
||||
Id: record.Fields["id"].GetStringValue(),
|
||||
Name: record.Fields["name"].GetStringValue(),
|
||||
Icon: icon,
|
||||
Identity: record.Fields["identity"].GetStringValue(),
|
||||
GlobalName: record.Fields["globalName"].GetStringValue(),
|
||||
Role: model.ParticipantPermissions_name[int32(record.Fields["participantPermissions"].GetNumberValue())],
|
||||
}
|
||||
|
||||
members = append(members, member)
|
||||
}
|
||||
|
||||
respondWithPagination(c, http.StatusOK, members, len(resp.Records), offset, limit, hasMore)
|
||||
}
|
||||
|
||||
// getObjectsHandler retrieves objects in a specific space
|
||||
//
|
||||
// @Summary Retrieve objects in a specific space
|
||||
|
@ -342,11 +139,11 @@ func (a *ApiServer) getObjectsHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
paginatedObjects, hasMore := paginate(resp.Records, offset, limit)
|
||||
paginatedObjects, hasMore := pagination.Paginate(resp.Records, offset, limit)
|
||||
objects := make([]Object, 0, len(paginatedObjects))
|
||||
|
||||
for _, record := range paginatedObjects {
|
||||
icon := a.getIconFromEmojiOrImage(record.Fields["iconEmoji"].GetStringValue(), record.Fields["iconImage"].GetStringValue())
|
||||
icon := utils.GetIconFromEmojiOrImage(a.accountInfo, record.Fields["iconEmoji"].GetStringValue(), record.Fields["iconImage"].GetStringValue())
|
||||
objectTypeName, statusCode, errorMessage := a.resolveTypeToName(spaceId, record.Fields["type"].GetStringValue())
|
||||
if statusCode != http.StatusOK {
|
||||
c.JSON(statusCode, gin.H{"message": errorMessage})
|
||||
|
@ -374,7 +171,7 @@ func (a *ApiServer) getObjectsHandler(c *gin.Context) {
|
|||
objects = append(objects, object)
|
||||
}
|
||||
|
||||
respondWithPagination(c, http.StatusOK, objects, len(resp.Records), offset, limit, hasMore)
|
||||
pagination.RespondWithPagination(c, http.StatusOK, objects, len(resp.Records), offset, limit, hasMore)
|
||||
}
|
||||
|
||||
// getObjectHandler retrieves a specific object in a space
|
||||
|
@ -556,7 +353,7 @@ func (a *ApiServer) getObjectTypesHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
paginatedTypes, hasMore := paginate(resp.Records, offset, limit)
|
||||
paginatedTypes, hasMore := pagination.Paginate(resp.Records, offset, limit)
|
||||
objectTypes := make([]ObjectType, 0, len(paginatedTypes))
|
||||
|
||||
for _, record := range paginatedTypes {
|
||||
|
@ -569,7 +366,7 @@ func (a *ApiServer) getObjectTypesHandler(c *gin.Context) {
|
|||
})
|
||||
}
|
||||
|
||||
respondWithPagination(c, http.StatusOK, objectTypes, len(resp.Records), offset, limit, hasMore)
|
||||
pagination.RespondWithPagination(c, http.StatusOK, objectTypes, len(resp.Records), offset, limit, hasMore)
|
||||
}
|
||||
|
||||
// getObjectTypeTemplatesHandler retrieves a list of templates for a specific object type in a space
|
||||
|
@ -649,7 +446,7 @@ func (a *ApiServer) getObjectTypeTemplatesHandler(c *gin.Context) {
|
|||
}
|
||||
|
||||
// Finally, open each template and populate the response
|
||||
paginatedTemplates, hasMore := paginate(templateIds, offset, limit)
|
||||
paginatedTemplates, hasMore := pagination.Paginate(templateIds, offset, limit)
|
||||
templates := make([]ObjectTemplate, 0, len(paginatedTemplates))
|
||||
|
||||
for _, templateId := range paginatedTemplates {
|
||||
|
@ -671,7 +468,7 @@ func (a *ApiServer) getObjectTypeTemplatesHandler(c *gin.Context) {
|
|||
})
|
||||
}
|
||||
|
||||
respondWithPagination(c, http.StatusOK, templates, len(templateIds), offset, limit, hasMore)
|
||||
pagination.RespondWithPagination(c, http.StatusOK, templates, len(templateIds), offset, limit, hasMore)
|
||||
}
|
||||
|
||||
// searchHandler searches and retrieves objects across all the spaces
|
||||
|
@ -796,7 +593,7 @@ func (a *ApiServer) searchHandler(c *gin.Context) {
|
|||
}
|
||||
|
||||
for _, record := range objectResp.Records {
|
||||
icon := a.getIconFromEmojiOrImage(record.Fields["iconEmoji"].GetStringValue(), record.Fields["iconImage"].GetStringValue())
|
||||
icon := utils.GetIconFromEmojiOrImage(a.accountInfo, record.Fields["iconEmoji"].GetStringValue(), record.Fields["iconImage"].GetStringValue())
|
||||
objectTypeName, statusCode, errorMessage := a.resolveTypeToName(spaceId, record.Fields["type"].GetStringValue())
|
||||
if statusCode != http.StatusOK {
|
||||
c.JSON(statusCode, gin.H{"message": errorMessage})
|
||||
|
@ -828,190 +625,7 @@ func (a *ApiServer) searchHandler(c *gin.Context) {
|
|||
})
|
||||
|
||||
// TODO: solve global pagination vs per space pagination
|
||||
paginatedResults, hasMore := paginate(searchResults, offset, limit)
|
||||
paginatedResults, hasMore := pagination.Paginate(searchResults, offset, limit)
|
||||
|
||||
respondWithPagination(c, http.StatusOK, paginatedResults, len(searchResults), offset, limit, hasMore)
|
||||
}
|
||||
|
||||
// getChatMessagesHandler retrieves last chat messages
|
||||
//
|
||||
// @Summary Retrieve last chat messages
|
||||
// @Tags chat
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "The ID of the space"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set"
|
||||
// @Param limit query int false "The number of items to return" default(100)
|
||||
// @Success 200 {object} map[string][]ChatMessage "List of chat messages"
|
||||
// @Failure 502 {object} ServerError "Internal server error"
|
||||
// @Router /v1/spaces/{space_id}/chat/messages [get]
|
||||
func (a *ApiServer) getChatMessagesHandler(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
// TODO: implement offset
|
||||
// offset := c.GetInt("offset")
|
||||
limit := c.GetInt("limit")
|
||||
|
||||
chatId, statusCode, errorMessage := a.getChatIdForSpace(spaceId)
|
||||
if statusCode != http.StatusOK {
|
||||
c.JSON(statusCode, gin.H{"message": errorMessage})
|
||||
return
|
||||
}
|
||||
|
||||
lastMessages := a.mw.ChatSubscribeLastMessages(c.Request.Context(), &pb.RpcChatSubscribeLastMessagesRequest{
|
||||
ChatObjectId: chatId,
|
||||
Limit: int32(limit),
|
||||
})
|
||||
|
||||
if lastMessages.Error.Code != pb.RpcChatSubscribeLastMessagesResponseError_NULL {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to retrieve last messages."})
|
||||
}
|
||||
|
||||
messages := make([]ChatMessage, 0, len(lastMessages.Messages))
|
||||
for _, message := range lastMessages.Messages {
|
||||
|
||||
attachments := make([]Attachment, 0, len(message.Attachments))
|
||||
for _, attachment := range message.Attachments {
|
||||
target := attachment.Target
|
||||
if attachment.Type != model.ChatMessageAttachment_LINK {
|
||||
target = a.getGatewayURLForMedia(attachment.Target, false)
|
||||
}
|
||||
attachments = append(attachments, Attachment{
|
||||
Target: target,
|
||||
Type: model.ChatMessageAttachmentAttachmentType_name[int32(attachment.Type)],
|
||||
})
|
||||
}
|
||||
|
||||
messages = append(messages, ChatMessage{
|
||||
Type: "chat_message",
|
||||
Id: message.Id,
|
||||
Creator: message.Creator,
|
||||
CreatedAt: message.CreatedAt,
|
||||
ReplyToMessageId: message.ReplyToMessageId,
|
||||
Message: MessageContent{
|
||||
Text: message.Message.Text,
|
||||
// TODO: params
|
||||
// Style: nil,
|
||||
// Marks: nil,
|
||||
},
|
||||
Attachments: attachments,
|
||||
Reactions: Reactions{
|
||||
ReactionsMap: func() map[string]IdentityList {
|
||||
reactionsMap := make(map[string]IdentityList)
|
||||
for emoji, ids := range message.Reactions.Reactions {
|
||||
reactionsMap[emoji] = IdentityList{Ids: ids.Ids}
|
||||
}
|
||||
return reactionsMap
|
||||
}(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"chatId": chatId, "messages": messages})
|
||||
}
|
||||
|
||||
// getChatMessageHandler retrieves a specific chat message by message_id
|
||||
//
|
||||
// @Summary Retrieve a specific chat message
|
||||
// @Tags chat
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "The ID of the space"
|
||||
// @Param message_id path string true "Message ID"
|
||||
// @Success 200 {object} ChatMessage "Chat message"
|
||||
// @Failure 404 {object} NotFoundError "Message not found"
|
||||
// @Failure 502 {object} ServerError "Internal server error"
|
||||
// @Router /v1/spaces/{space_id}/chat/messages/{message_id} [get]
|
||||
func (a *ApiServer) getChatMessageHandler(c *gin.Context) {
|
||||
// TODO: Implement logic to retrieve a specific chat message by message_id
|
||||
|
||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Not implemented yet"})
|
||||
}
|
||||
|
||||
// addChatMessageHandler adds a new chat message to chat
|
||||
//
|
||||
// @Summary Add a new chat message
|
||||
// @Tags chat
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "The ID of the space"
|
||||
// @Param message body ChatMessage true "Chat message"
|
||||
// @Success 201 {object} ChatMessage "Created chat message"
|
||||
// @Failure 400 {object} ValidationError "Invalid input"
|
||||
// @Failure 502 {object} ServerError "Internal server error"
|
||||
// @Router /v1/spaces/{space_id}/chat/messages [post]
|
||||
func (a *ApiServer) addChatMessageHandler(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
|
||||
request := AddMessageRequest{}
|
||||
if err := c.BindJSON(&request); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"message": "Invalid JSON"})
|
||||
return
|
||||
}
|
||||
|
||||
chatId, statusCode, errorMessage := a.getChatIdForSpace(spaceId)
|
||||
if statusCode != http.StatusOK {
|
||||
c.JSON(statusCode, gin.H{"message": errorMessage})
|
||||
return
|
||||
}
|
||||
|
||||
resp := a.mw.ChatAddMessage(c.Request.Context(), &pb.RpcChatAddMessageRequest{
|
||||
ChatObjectId: chatId,
|
||||
Message: &model.ChatMessage{
|
||||
Id: "",
|
||||
OrderId: "",
|
||||
Creator: "",
|
||||
CreatedAt: 0,
|
||||
ModifiedAt: 0,
|
||||
ReplyToMessageId: "",
|
||||
Message: &model.ChatMessageMessageContent{
|
||||
Text: request.Text,
|
||||
// TODO: param
|
||||
// Style: request.Style,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcChatAddMessageResponseError_NULL {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to create message."})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"messageId": resp.MessageId})
|
||||
}
|
||||
|
||||
// updateChatMessageHandler updates an existing chat message by message_id
|
||||
//
|
||||
// @Summary Update an existing chat message
|
||||
// @Tags chat
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "The ID of the space"
|
||||
// @Param message_id path string true "Message ID"
|
||||
// @Param message body ChatMessage true "Chat message"
|
||||
// @Success 200 {object} ChatMessage "Updated chat message"
|
||||
// @Failure 400 {object} ValidationError "Invalid input"
|
||||
// @Failure 404 {object} NotFoundError "Message not found"
|
||||
// @Failure 502 {object} ServerError "Internal server error"
|
||||
// @Router /v1/spaces/{space_id}/chat/messages/{message_id} [put]
|
||||
func (a *ApiServer) updateChatMessageHandler(c *gin.Context) {
|
||||
// TODO: Implement logic to update an existing chat message by message_id
|
||||
|
||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Not implemented yet"})
|
||||
}
|
||||
|
||||
// deleteChatMessageHandler deletes a chat message by message_id
|
||||
//
|
||||
// @Summary Delete a chat message
|
||||
// @Tags chat
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "The ID of the space"
|
||||
// @Param message_id path string true "Message ID"
|
||||
// @Success 204 "Message deleted successfully"
|
||||
// @Failure 404 {object} NotFoundError "Message not found"
|
||||
// @Failure 502 {object} ServerError "Internal server error"
|
||||
// @Router /v1/spaces/{space_id}/chat/messages/{message_id} [delete]
|
||||
func (a *ApiServer) deleteChatMessageHandler(c *gin.Context) {
|
||||
// TODO: Implement logic to delete a chat message by message_id
|
||||
|
||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Not implemented yet"})
|
||||
pagination.RespondWithPagination(c, http.StatusOK, paginatedResults, len(searchResults), offset, limit, hasMore)
|
||||
}
|
||||
|
|
|
@ -2,27 +2,16 @@ package api
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/anyproto/anytype-heart/cmd/api/utils"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
)
|
||||
|
||||
// getGatewayURLForMedia returns the URL of file gateway for the media object with the given ID
|
||||
func (a *ApiServer) getGatewayURLForMedia(objectId string, isIcon bool) string {
|
||||
widthParam := ""
|
||||
if isIcon {
|
||||
widthParam = "?width=100"
|
||||
}
|
||||
return fmt.Sprintf("%s/image/%s%s", a.accountInfo.GatewayUrl, objectId, widthParam)
|
||||
}
|
||||
|
||||
// resolveTypeToName resolves the type ID to the name of the type, e.g. "ot-page" to "Page" or "bafyreigyb6l5szohs32ts26ku2j42yd65e6hqy2u3gtzgdwqv6hzftsetu" to "Custom Type"
|
||||
func (a *ApiServer) resolveTypeToName(spaceId string, typeId string) (typeName string, statusCode int, errorMessage string) {
|
||||
// Can't look up preinstalled types based on relation key, therefore need to use unique key
|
||||
|
@ -54,71 +43,6 @@ func (a *ApiServer) resolveTypeToName(spaceId string, typeId string) (typeName s
|
|||
return resp.Records[0].Fields["name"].GetStringValue(), http.StatusOK, ""
|
||||
}
|
||||
|
||||
// getChatIdForSpace returns the chat ID for the space with the given ID
|
||||
func (a *ApiServer) getChatIdForSpace(spaceId string) (chatId string, statusCode int, errorMessage string) {
|
||||
workspace, statusCode, errorMessage := a.getWorkspaceInfo(spaceId)
|
||||
if statusCode != http.StatusOK {
|
||||
return "", statusCode, errorMessage
|
||||
}
|
||||
|
||||
resp := a.mw.ObjectShow(context.Background(), &pb.RpcObjectShowRequest{
|
||||
SpaceId: spaceId,
|
||||
ObjectId: workspace.WorkspaceObjectId,
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return "", http.StatusInternalServerError, "Failed to open workspace object."
|
||||
}
|
||||
|
||||
if !resp.ObjectView.Details[0].Details.Fields["hasChat"].GetBoolValue() {
|
||||
return "", http.StatusNotFound, "Chat not found."
|
||||
}
|
||||
|
||||
return resp.ObjectView.Details[0].Details.Fields["chatId"].GetStringValue(), http.StatusOK, ""
|
||||
}
|
||||
|
||||
// getWorkspaceInfo returns the workspace info for the space with the given ID
|
||||
func (a *ApiServer) getWorkspaceInfo(spaceId string) (space Space, statusCode int, errorMessage string) {
|
||||
workspaceResponse := a.mw.WorkspaceOpen(context.Background(), &pb.RpcWorkspaceOpenRequest{
|
||||
SpaceId: spaceId,
|
||||
WithChat: true,
|
||||
})
|
||||
|
||||
if workspaceResponse.Error.Code != pb.RpcWorkspaceOpenResponseError_NULL {
|
||||
return Space{}, http.StatusInternalServerError, "Failed to open workspace."
|
||||
}
|
||||
|
||||
return Space{
|
||||
Type: "space",
|
||||
Id: spaceId,
|
||||
HomeObjectId: workspaceResponse.Info.HomeObjectId,
|
||||
ArchiveObjectId: workspaceResponse.Info.ArchiveObjectId,
|
||||
ProfileObjectId: workspaceResponse.Info.ProfileObjectId,
|
||||
MarketplaceWorkspaceId: workspaceResponse.Info.MarketplaceWorkspaceId,
|
||||
WorkspaceObjectId: workspaceResponse.Info.WorkspaceObjectId,
|
||||
DeviceId: workspaceResponse.Info.DeviceId,
|
||||
AccountSpaceId: workspaceResponse.Info.AccountSpaceId,
|
||||
WidgetsId: workspaceResponse.Info.WidgetsId,
|
||||
SpaceViewId: workspaceResponse.Info.SpaceViewId,
|
||||
TechSpaceId: workspaceResponse.Info.TechSpaceId,
|
||||
Timezone: workspaceResponse.Info.TimeZone,
|
||||
NetworkId: workspaceResponse.Info.NetworkId,
|
||||
}, http.StatusOK, ""
|
||||
}
|
||||
|
||||
// getIconFromEmojiOrImage returns the icon to use for the object, which can be either an emoji or an image url
|
||||
func (a *ApiServer) getIconFromEmojiOrImage(iconEmoji string, iconImage string) string {
|
||||
if iconEmoji != "" {
|
||||
return iconEmoji
|
||||
}
|
||||
|
||||
if iconImage != "" {
|
||||
return a.getGatewayURLForMedia(iconImage, true)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// getBlocks returns the blocks of the object
|
||||
func (a *ApiServer) getBlocks(resp *pb.RpcObjectShowResponse) []Block {
|
||||
blocks := []Block{}
|
||||
|
@ -134,7 +58,7 @@ func (a *ApiServer) getBlocks(resp *pb.RpcObjectShowResponse) []Block {
|
|||
Style: model.BlockContentTextStyle_name[int32(content.Text.Style)],
|
||||
Checked: content.Text.Checked,
|
||||
Color: content.Text.Color,
|
||||
Icon: a.getIconFromEmojiOrImage(content.Text.IconEmoji, content.Text.IconImage),
|
||||
Icon: utils.GetIconFromEmojiOrImage(a.accountInfo, content.Text.IconEmoji, content.Text.IconImage),
|
||||
}
|
||||
case *model.BlockContentOfFile:
|
||||
file = &File{
|
||||
|
@ -241,34 +165,3 @@ func (a *ApiServer) getTags(resp *pb.RpcObjectShowResponse) []Tag {
|
|||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
// respondWithPagination returns a json response with the paginated data and corresponding metadata
|
||||
func respondWithPagination[T any](c *gin.Context, statusCode int, data []T, total, offset, limit int, hasMore bool) {
|
||||
c.JSON(statusCode, PaginatedResponse[T]{
|
||||
Data: data,
|
||||
Pagination: PaginationMeta{
|
||||
Total: total,
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
HasMore: hasMore,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// paginate paginates the given records based on the offset and limit
|
||||
func paginate[T any](records []T, offset, limit int) ([]T, bool) {
|
||||
total := len(records)
|
||||
start := offset
|
||||
end := offset + limit
|
||||
|
||||
if start > total {
|
||||
start = total
|
||||
}
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
|
||||
paginated := records[start:end]
|
||||
hasMore := end < total
|
||||
return paginated, hasMore
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/webstradev/gin-pagination/v2/pkg/pagination"
|
||||
|
||||
_ "github.com/anyproto/anytype-heart/cmd/api/docs"
|
||||
"github.com/anyproto/anytype-heart/cmd/api/space"
|
||||
"github.com/anyproto/anytype-heart/core"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
|
@ -29,8 +30,8 @@ type ApiServer struct {
|
|||
router *gin.Engine
|
||||
server *http.Server
|
||||
|
||||
// init after app start
|
||||
accountInfo *model.AccountInfo
|
||||
accountInfo *model.AccountInfo
|
||||
spaceService *space.SpaceService
|
||||
}
|
||||
|
||||
// TODO: User represents an authenticated user with permissions
|
||||
|
@ -41,9 +42,10 @@ type User struct {
|
|||
|
||||
func newApiServer(mw service.ClientCommandsServer, mwInternal core.MiddlewareInternal) *ApiServer {
|
||||
a := &ApiServer{
|
||||
mw: mw,
|
||||
mwInternal: mwInternal,
|
||||
router: gin.Default(),
|
||||
mw: mw,
|
||||
mwInternal: mwInternal,
|
||||
router: gin.Default(),
|
||||
spaceService: space.NewService(mw),
|
||||
}
|
||||
|
||||
a.server = &http.Server{
|
||||
|
@ -100,8 +102,8 @@ func RunApiServer(ctx context.Context, mw service.ClientCommandsServer, mwIntern
|
|||
// readOnly.Use(a.AuthMiddleware())
|
||||
// readOnly.Use(a.PermissionMiddleware("read-only"))
|
||||
{
|
||||
readOnly.GET("/spaces", paginator, a.getSpacesHandler)
|
||||
readOnly.GET("/spaces/:space_id/members", paginator, a.getMembersHandler)
|
||||
readOnly.GET("/spaces", paginator, space.GetSpacesHandler(a.spaceService))
|
||||
readOnly.GET("/spaces/:space_id/members", paginator, space.GetMembersHandler(a.spaceService))
|
||||
readOnly.GET("/spaces/:space_id/objects", paginator, a.getObjectsHandler)
|
||||
readOnly.GET("/spaces/:space_id/objects/:object_id", a.getObjectHandler)
|
||||
readOnly.GET("/spaces/:space_id/objectTypes", paginator, a.getObjectTypesHandler)
|
||||
|
@ -114,23 +116,11 @@ func RunApiServer(ctx context.Context, mw service.ClientCommandsServer, mwIntern
|
|||
// readWrite.Use(a.AuthMiddleware())
|
||||
// readWrite.Use(a.PermissionMiddleware("read-write"))
|
||||
{
|
||||
readWrite.POST("/spaces", a.createSpaceHandler)
|
||||
// readWrite.POST("/spaces", a.createSpaceHandler)
|
||||
readWrite.POST("/spaces/:space_id/objects", a.createObjectHandler)
|
||||
readWrite.PUT("/spaces/:space_id/objects/:object_id", a.updateObjectHandler)
|
||||
}
|
||||
|
||||
// Chat routes
|
||||
chat := a.router.Group("/v1/spaces/:space_id/chat")
|
||||
// chat.Use(a.AuthMiddleware())
|
||||
// chat.Use(a.PermissionMiddleware("read-write"))
|
||||
{
|
||||
chat.GET("/messages", paginator, a.getChatMessagesHandler)
|
||||
chat.GET("/messages/:message_id", a.getChatMessageHandler)
|
||||
chat.POST("/messages", a.addChatMessageHandler)
|
||||
chat.PUT("/messages/:message_id", a.updateChatMessageHandler)
|
||||
chat.DELETE("/messages/:message_id", a.deleteChatMessageHandler)
|
||||
}
|
||||
|
||||
// Start the HTTP server
|
||||
go func() {
|
||||
if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
|
|
|
@ -27,6 +27,7 @@ func (a *ApiServer) initAccountInfo() gin.HandlerFunc {
|
|||
}
|
||||
|
||||
a.accountInfo = accInfo
|
||||
a.spaceService.AccountInfo = accInfo
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
|
13
cmd/api/pagination/model.go
Normal file
13
cmd/api/pagination/model.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package pagination
|
||||
|
||||
type PaginationMeta struct {
|
||||
Total int `json:"total" example:"1024"` // the total number of items available on that endpoint
|
||||
Offset int `json:"offset" example:"0"` // the current offset
|
||||
Limit int `json:"limit" example:"100"` // the current limit
|
||||
HasMore bool `json:"has_more" example:"true"` // whether there are more items available
|
||||
}
|
||||
|
||||
type PaginatedResponse[T any] struct {
|
||||
Data []T `json:"data"`
|
||||
Pagination PaginationMeta `json:"pagination"`
|
||||
}
|
39
cmd/api/pagination/pagination.go
Normal file
39
cmd/api/pagination/pagination.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package pagination
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type Service[T any] interface {
|
||||
RespondWithPagination(c *gin.Context, statusCode int, data []T, total, offset, limit int, hasMore bool)
|
||||
Paginate(records []T, offset, limit int) ([]T, bool)
|
||||
}
|
||||
|
||||
// RespondWithPagination returns a json response with the paginated data and corresponding metadata
|
||||
func RespondWithPagination[T any](c *gin.Context, statusCode int, data []T, total, offset, limit int, hasMore bool) {
|
||||
c.JSON(statusCode, PaginatedResponse[T]{
|
||||
Data: data,
|
||||
Pagination: PaginationMeta{
|
||||
Total: total,
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
HasMore: hasMore,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Paginate paginates the given records based on the offset and limit
|
||||
func Paginate[T any](records []T, offset, limit int) ([]T, bool) {
|
||||
total := len(records)
|
||||
start := offset
|
||||
end := offset + limit
|
||||
|
||||
if start > total {
|
||||
start = total
|
||||
}
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
|
||||
paginated := records[start:end]
|
||||
hasMore := end < total
|
||||
return paginated, hasMore
|
||||
}
|
|
@ -1,17 +1,5 @@
|
|||
package api
|
||||
|
||||
type PaginationMeta struct {
|
||||
Total int `json:"total" example:"1024"` // the total number of items available on that endpoint
|
||||
Offset int `json:"offset" example:"0"` // the current offset
|
||||
Limit int `json:"limit" example:"100"` // the current limit
|
||||
HasMore bool `json:"has_more" example:"true"` // whether there are more items available
|
||||
}
|
||||
|
||||
type PaginatedResponse[T any] struct {
|
||||
Data []T `json:"data"`
|
||||
Pagination PaginationMeta `json:"pagination"`
|
||||
}
|
||||
|
||||
type AuthDisplayCodeResponse struct {
|
||||
ChallengeId string `json:"challenge_id" example:"67647f5ecda913e9a2e11b26"`
|
||||
}
|
||||
|
@ -21,40 +9,6 @@ type AuthTokenResponse struct {
|
|||
AppKey string `json:"app_key" example:""`
|
||||
}
|
||||
|
||||
type CreateSpaceResponse struct {
|
||||
SpaceId string `json:"space_id" example:"bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"`
|
||||
Name string `json:"name" example:"Space Name"`
|
||||
}
|
||||
|
||||
type Space struct {
|
||||
Type string `json:"type" example:"space"`
|
||||
Id string `json:"id" example:"bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"`
|
||||
Name string `json:"name" example:"Space Name"`
|
||||
Icon string `json:"icon" example:"http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"`
|
||||
HomeObjectId string `json:"home_object_id" example:"bafyreie4qcl3wczb4cw5hrfyycikhjyh6oljdis3ewqrk5boaav3sbwqya"`
|
||||
ArchiveObjectId string `json:"archive_object_id" example:"bafyreialsgoyflf3etjm3parzurivyaukzivwortf32b4twnlwpwocsrri"`
|
||||
ProfileObjectId string `json:"profile_object_id" example:"bafyreiaxhwreshjqwndpwtdsu4mtihaqhhmlygqnyqpfyfwlqfq3rm3gw4"`
|
||||
MarketplaceWorkspaceId string `json:"marketplace_workspace_id" example:"_anytype_marketplace"`
|
||||
WorkspaceObjectId string `json:"workspace_object_id" example:"bafyreiapey2g6e6za4zfxvlgwdy4hbbfu676gmwrhnqvjbxvrchr7elr3y"`
|
||||
DeviceId string `json:"device_id" example:"12D3KooWGZMJ4kQVyQVXaj7gJPZr3RZ2nvd9M2Eq2pprEoPih9WF"`
|
||||
AccountSpaceId string `json:"account_space_id" example:"bafyreihpd2knon5wbljhtfeg3fcqtg3i2pomhhnigui6lrjmzcjzep7gcy.23me69r569oi1"`
|
||||
WidgetsId string `json:"widgets_id" example:"bafyreialj7pceh53mifm5dixlho47ke4qjmsn2uh4wsjf7xq2pnlo5xfva"`
|
||||
SpaceViewId string `json:"space_view_id" example:"bafyreigzv3vq7qwlrsin6njoduq727ssnhwd6bgyfj6nm4hv3pxoc2rxhy"`
|
||||
TechSpaceId string `json:"tech_space_id" example:"bafyreif4xuwncrjl6jajt4zrrfnylpki476nv2w64yf42ovt7gia7oypii.23me69r569oi1"`
|
||||
Timezone string `json:"timezone" example:""`
|
||||
NetworkId string `json:"network_id" example:"N83gJpVd9MuNRZAuJLZ7LiMntTThhPc6DtzWWVjb1M3PouVU"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
Type string `json:"type" example:"member"`
|
||||
Id string `json:"id" example:"_participant_bafyreigyfkt6rbv24sbv5aq2hko1bhmv5xxlf22b4bypdu6j7hnphm3psq_23me69r569oi1_AAjEaEwPF4nkEh9AWkqEnzcQ8HziBB4ETjiTpvRCQvWnSMDZ"`
|
||||
Name string `json:"name" example:"John Doe"`
|
||||
Icon string `json:"icon" example:"http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"`
|
||||
Identity string `json:"identity" example:"AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ"`
|
||||
GlobalName string `json:"global_name" example:"john.any"`
|
||||
Role string `json:"role" enum:"Reader,Writer,Owner,NoPermission" example:"Owner"`
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
Type string `json:"type" example:"object"`
|
||||
Id string `json:"id" example:"bafyreie6n5l5nkbjal37su54cha4coy7qzuhrnajluzv5qd5jvtsrxkequ"`
|
||||
|
@ -123,38 +77,6 @@ type ObjectTemplate struct {
|
|||
Icon string `json:"icon" example:"📄"`
|
||||
}
|
||||
|
||||
type ChatMessage struct {
|
||||
Type string `json:"chat_message"`
|
||||
Id string `json:"id"` // Unique message identifier
|
||||
OrderId string `json:"order_id"` // Used for subscriptions
|
||||
Creator string `json:"creator"` // Identifier for the message creator
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
ModifiedAt int64 `json:"modified_at"`
|
||||
ReplyToMessageId string `json:"reply_to_message_id"` // Identifier for the message being replied to
|
||||
Message MessageContent `json:"message"` // Message content
|
||||
Attachments []Attachment `json:"attachments"` // Attachments slice
|
||||
Reactions Reactions `json:"reactions"` // Reactions to the message
|
||||
}
|
||||
|
||||
type MessageContent struct {
|
||||
Text string `json:"text"` // The text content of the message part
|
||||
Style string `json:"style"` // The style/type of the message part
|
||||
Marks []string `json:"marks"` // List of marks applied to the text
|
||||
}
|
||||
|
||||
type Attachment struct {
|
||||
Target string `json:"target"` // Identifier for the attachment object
|
||||
Type string `json:"type"` // Type of attachment
|
||||
}
|
||||
|
||||
type Reactions struct {
|
||||
ReactionsMap map[string]IdentityList `json:"reactions"` // Map of emoji to list of user IDs
|
||||
}
|
||||
|
||||
type IdentityList struct {
|
||||
Ids []string `json:"ids"` // List of user IDs
|
||||
}
|
||||
|
||||
type ServerError struct {
|
||||
Error struct {
|
||||
Message string `json:"message"`
|
||||
|
|
124
cmd/api/space/handler.go
Normal file
124
cmd/api/space/handler.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package space
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/anyproto/anytype-heart/cmd/api/pagination"
|
||||
)
|
||||
|
||||
// GetSpacesHandler retrieves a list of spaces
|
||||
//
|
||||
// @Summary Retrieve a list of spaces
|
||||
// @Tags spaces
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set"
|
||||
// @Param limit query int false "The number of items to return" default(100)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Space] "List of spaces"
|
||||
// @Failure 403 {object} api.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} api.NotFoundError "Resource not found"
|
||||
// @Failure 502 {object} api.ServerError "Internal server error"
|
||||
// @Router /spaces [get]
|
||||
func GetSpacesHandler(s *SpaceService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
offset := c.GetInt("offset")
|
||||
limit := c.GetInt("limit")
|
||||
|
||||
spaces, total, hasMore, err := s.ListSpaces(c.Request.Context(), offset, limit)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, ErrNoSpacesFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"message": "No spaces found."})
|
||||
return
|
||||
case errors.Is(err, ErrFailedListSpaces):
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to retrieve list of spaces."})
|
||||
return
|
||||
case errors.Is(err, ErrFailedOpenWorkspace):
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to open workspace."})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pagination.RespondWithPagination(c, http.StatusOK, spaces, total, offset, limit, hasMore)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSpaceHandler creates a new space
|
||||
//
|
||||
// @Summary Create a new Space
|
||||
// @Tags spaces
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param name body string true "Space Name"
|
||||
// @Success 200 {object} CreateSpaceResponse "Space created successfully"
|
||||
// @Failure 403 {object} api.UnauthorizedError "Unauthorized"
|
||||
// @Failure 502 {object} api.ServerError "Internal server error"
|
||||
// @Router /spaces [post]
|
||||
func CreateSpaceHandler(s *SpaceService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
nameRequest := CreateSpaceRequest{}
|
||||
if err := c.BindJSON(&nameRequest); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"message": "Invalid JSON"})
|
||||
return
|
||||
}
|
||||
name := nameRequest.Name
|
||||
|
||||
space, err := s.CreateSpace(c.Request.Context(), name)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, ErrFailedCreateSpace):
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to create space."})
|
||||
return
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, CreateSpaceResponse{Space: space})
|
||||
}
|
||||
}
|
||||
|
||||
// GetMembersHandler retrieves a list of members for the specified space
|
||||
//
|
||||
// @Summary Retrieve a list of members for the specified Space
|
||||
// @Tags spaces
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "The ID of the space"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set"
|
||||
// @Param limit query int false "The number of items to return" default(100)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Member] "List of members"
|
||||
// @Failure 403 {object} api.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} api.NotFoundError "Resource not found"
|
||||
// @Failure 502 {object} api.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/members [get]
|
||||
func GetMembersHandler(s *SpaceService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
offset := c.GetInt("offset")
|
||||
limit := c.GetInt("limit")
|
||||
|
||||
members, total, hasMore, err := s.ListMembers(c.Request.Context(), spaceId, offset, limit)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, ErrNoMembersFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"message": "No members found."})
|
||||
return
|
||||
case errors.Is(err, ErrFailedListMembers):
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to retrieve list of members."})
|
||||
return
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pagination.RespondWithPagination(c, http.StatusOK, members, total, offset, limit, hasMore)
|
||||
}
|
||||
}
|
41
cmd/api/space/model.go
Normal file
41
cmd/api/space/model.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package space
|
||||
|
||||
type CreateSpaceRequest struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type CreateSpaceResponse struct {
|
||||
Space Space `json:"space"`
|
||||
}
|
||||
|
||||
type Space struct {
|
||||
Type string `json:"type" example:"space"`
|
||||
Id string `json:"id" example:"bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"`
|
||||
Name string `json:"name" example:"Space Name"`
|
||||
Icon string `json:"icon" example:"http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"`
|
||||
HomeObjectId string `json:"home_object_id" example:"bafyreie4qcl3wczb4cw5hrfyycikhjyh6oljdis3ewqrk5boaav3sbwqya"`
|
||||
ArchiveObjectId string `json:"archive_object_id" example:"bafyreialsgoyflf3etjm3parzurivyaukzivwortf32b4twnlwpwocsrri"`
|
||||
ProfileObjectId string `json:"profile_object_id" example:"bafyreiaxhwreshjqwndpwtdsu4mtihaqhhmlygqnyqpfyfwlqfq3rm3gw4"`
|
||||
MarketplaceWorkspaceId string `json:"marketplace_workspace_id" example:"_anytype_marketplace"`
|
||||
WorkspaceObjectId string `json:"workspace_object_id" example:"bafyreiapey2g6e6za4zfxvlgwdy4hbbfu676gmwrhnqvjbxvrchr7elr3y"`
|
||||
DeviceId string `json:"device_id" example:"12D3KooWGZMJ4kQVyQVXaj7gJPZr3RZ2nvd9M2Eq2pprEoPih9WF"`
|
||||
AccountSpaceId string `json:"account_space_id" example:"bafyreihpd2knon5wbljhtfeg3fcqtg3i2pomhhnigui6lrjmzcjzep7gcy.23me69r569oi1"`
|
||||
WidgetsId string `json:"widgets_id" example:"bafyreialj7pceh53mifm5dixlho47ke4qjmsn2uh4wsjf7xq2pnlo5xfva"`
|
||||
SpaceViewId string `json:"space_view_id" example:"bafyreigzv3vq7qwlrsin6njoduq727ssnhwd6bgyfj6nm4hv3pxoc2rxhy"`
|
||||
TechSpaceId string `json:"tech_space_id" example:"bafyreif4xuwncrjl6jajt4zrrfnylpki476nv2w64yf42ovt7gia7oypii.23me69r569oi1"`
|
||||
GatewayUrl string `json:"gateway_url" example:"http://127.0.0.1:31006"`
|
||||
LocalStoragePath string `json:"local_storage_path" example:"/Users/johndoe/Library/Application Support/Anytype/data/AAHTtt1wuQEnaYBNZ2Cyfcvs6DqPqxgn8VXDVk4avsUkMuha"`
|
||||
Timezone string `json:"timezone" example:""`
|
||||
AnalyticsId string `json:"analytics_id" example:"624aecdd-4797-4611-9d61-a2ae5f53cf1c"`
|
||||
NetworkId string `json:"network_id" example:"N83gJpVd9MuNRZAuJLZ7LiMntTThhPc6DtzWWVjb1M3PouVU"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
Type string `json:"type" example:"member"`
|
||||
Id string `json:"id" example:"_participant_bafyreigyfkt6rbv24sbv5aq2hko1bhmv5xxlf22b4bypdu6j7hnphm3psq_23me69r569oi1_AAjEaEwPF4nkEh9AWkqEnzcQ8HziBB4ETjiTpvRCQvWnSMDZ"`
|
||||
Name string `json:"name" example:"John Doe"`
|
||||
Icon string `json:"icon" example:"http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"`
|
||||
Identity string `json:"identity" example:"AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ"`
|
||||
GlobalName string `json:"global_name" example:"john.any"`
|
||||
Role string `json:"role" enum:"Reader,Writer,Owner,NoPermission" example:"Owner"`
|
||||
}
|
214
cmd/api/space/service.go
Normal file
214
cmd/api/space/service.go
Normal file
|
@ -0,0 +1,214 @@
|
|||
package space
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/gogo/protobuf/types"
|
||||
|
||||
"github.com/anyproto/anytype-heart/cmd/api/pagination"
|
||||
"github.com/anyproto/anytype-heart/cmd/api/utils"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoSpacesFound = errors.New("no spaces found")
|
||||
ErrFailedListSpaces = errors.New("failed to retrieve list of spaces")
|
||||
ErrFailedOpenWorkspace = errors.New("failed to open workspace")
|
||||
ErrFailedGenerateRandomIcon = errors.New("failed to generate random icon")
|
||||
ErrFailedCreateSpace = errors.New("failed to create space")
|
||||
ErrNoMembersFound = errors.New("no members found")
|
||||
ErrFailedListMembers = errors.New("failed to retrieve list of members")
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
ListSpaces(ctx context.Context) ([]Space, error)
|
||||
CreateSpace(ctx context.Context, name string) (Space, error)
|
||||
}
|
||||
|
||||
type SpaceService struct {
|
||||
mw service.ClientCommandsServer
|
||||
AccountInfo *model.AccountInfo
|
||||
}
|
||||
|
||||
func NewService(mw service.ClientCommandsServer) *SpaceService {
|
||||
return &SpaceService{mw: mw}
|
||||
}
|
||||
|
||||
func (s *SpaceService) ListSpaces(ctx context.Context, offset int, limit int) (spaces []Space, total int, hasMore bool, err error) {
|
||||
resp := s.mw.ObjectSearch(ctx, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: s.AccountInfo.TechSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_spaceView)),
|
||||
},
|
||||
{
|
||||
RelationKey: bundle.RelationKeySpaceLocalStatus.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.SpaceStatus_Ok)),
|
||||
},
|
||||
{
|
||||
RelationKey: bundle.RelationKeySpaceRemoteStatus.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.SpaceStatus_Ok)),
|
||||
},
|
||||
},
|
||||
Sorts: []*model.BlockContentDataviewSort{
|
||||
{
|
||||
RelationKey: "spaceOrder",
|
||||
Type: model.BlockContentDataviewSort_Asc,
|
||||
NoCollate: true,
|
||||
EmptyPlacement: model.BlockContentDataviewSort_End,
|
||||
},
|
||||
},
|
||||
Keys: []string{"targetSpaceId", "name", "iconEmoji", "iconImage"},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
return nil, 0, false, ErrFailedListSpaces
|
||||
}
|
||||
|
||||
if len(resp.Records) == 0 {
|
||||
return nil, 0, false, ErrNoSpacesFound
|
||||
}
|
||||
|
||||
total = len(resp.Records)
|
||||
paginatedRecords, hasMore := pagination.Paginate(resp.Records, offset, limit)
|
||||
spaces = make([]Space, 0, len(paginatedRecords))
|
||||
|
||||
for _, record := range paginatedRecords {
|
||||
workspace, err := s.getWorkspaceInfo(record.Fields["targetSpaceId"].GetStringValue())
|
||||
if err != nil {
|
||||
return nil, 0, false, err
|
||||
}
|
||||
|
||||
// TODO: name and icon are only returned here; fix that
|
||||
workspace.Name = record.Fields["name"].GetStringValue()
|
||||
workspace.Icon = utils.GetIconFromEmojiOrImage(s.AccountInfo, record.Fields["iconEmoji"].GetStringValue(), record.Fields["iconImage"].GetStringValue())
|
||||
|
||||
spaces = append(spaces, workspace)
|
||||
}
|
||||
|
||||
return spaces, total, hasMore, nil
|
||||
}
|
||||
|
||||
func (s *SpaceService) CreateSpace(ctx context.Context, name string) (Space, error) {
|
||||
iconOption, err := rand.Int(rand.Reader, big.NewInt(13))
|
||||
if err != nil {
|
||||
return Space{}, ErrFailedGenerateRandomIcon
|
||||
}
|
||||
|
||||
// Create new workspace with a random icon and import default use case
|
||||
resp := s.mw.WorkspaceCreate(ctx, &pb.RpcWorkspaceCreateRequest{
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
"iconOption": pbtypes.Float64(float64(iconOption.Int64())),
|
||||
"name": pbtypes.String(name),
|
||||
"spaceDashboardId": pbtypes.String("lastOpened"),
|
||||
},
|
||||
},
|
||||
UseCase: pb.RpcObjectImportUseCaseRequest_GET_STARTED,
|
||||
WithChat: true,
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcWorkspaceCreateResponseError_NULL {
|
||||
return Space{}, ErrFailedCreateSpace
|
||||
}
|
||||
|
||||
return s.getWorkspaceInfo(resp.SpaceId)
|
||||
}
|
||||
|
||||
func (s *SpaceService) ListMembers(ctx context.Context, spaceId string, offset int, limit int) (members []Member, total int, hasMore bool, err error) {
|
||||
resp := s.mw.ObjectSearch(ctx, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_participant)),
|
||||
},
|
||||
{
|
||||
RelationKey: bundle.RelationKeyParticipantStatus.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ParticipantStatus_Active)),
|
||||
},
|
||||
},
|
||||
Sorts: []*model.BlockContentDataviewSort{
|
||||
{
|
||||
RelationKey: "name",
|
||||
Type: model.BlockContentDataviewSort_Asc,
|
||||
},
|
||||
},
|
||||
Keys: []string{"id", "name", "iconEmoji", "iconImage", "identity", "globalName", "participantPermissions"},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
return nil, 0, false, ErrFailedListMembers
|
||||
}
|
||||
|
||||
if len(resp.Records) == 0 {
|
||||
return nil, 0, false, ErrNoMembersFound
|
||||
}
|
||||
|
||||
total = len(resp.Records)
|
||||
paginatedMembers, hasMore := pagination.Paginate(resp.Records, offset, limit)
|
||||
members = make([]Member, 0, len(paginatedMembers))
|
||||
|
||||
for _, record := range paginatedMembers {
|
||||
icon := utils.GetIconFromEmojiOrImage(s.AccountInfo, record.Fields["iconEmoji"].GetStringValue(), record.Fields["iconImage"].GetStringValue())
|
||||
|
||||
member := Member{
|
||||
Type: "space_member",
|
||||
Id: record.Fields["id"].GetStringValue(),
|
||||
Name: record.Fields["name"].GetStringValue(),
|
||||
Icon: icon,
|
||||
Identity: record.Fields["identity"].GetStringValue(),
|
||||
GlobalName: record.Fields["globalName"].GetStringValue(),
|
||||
Role: model.ParticipantPermissions_name[int32(record.Fields["participantPermissions"].GetNumberValue())],
|
||||
}
|
||||
|
||||
members = append(members, member)
|
||||
}
|
||||
|
||||
return members, total, hasMore, nil
|
||||
}
|
||||
|
||||
// getWorkspaceInfo returns the workspace info for the space with the given ID
|
||||
func (s *SpaceService) getWorkspaceInfo(spaceId string) (space Space, err error) {
|
||||
workspaceResponse := s.mw.WorkspaceOpen(context.Background(), &pb.RpcWorkspaceOpenRequest{
|
||||
SpaceId: spaceId,
|
||||
WithChat: true,
|
||||
})
|
||||
|
||||
if workspaceResponse.Error.Code != pb.RpcWorkspaceOpenResponseError_NULL {
|
||||
return Space{}, ErrFailedOpenWorkspace
|
||||
}
|
||||
|
||||
return Space{
|
||||
Type: "space",
|
||||
Id: spaceId,
|
||||
HomeObjectId: workspaceResponse.Info.HomeObjectId,
|
||||
ArchiveObjectId: workspaceResponse.Info.ArchiveObjectId,
|
||||
ProfileObjectId: workspaceResponse.Info.ProfileObjectId,
|
||||
MarketplaceWorkspaceId: workspaceResponse.Info.MarketplaceWorkspaceId,
|
||||
WorkspaceObjectId: workspaceResponse.Info.WorkspaceObjectId,
|
||||
DeviceId: workspaceResponse.Info.DeviceId,
|
||||
AccountSpaceId: workspaceResponse.Info.AccountSpaceId,
|
||||
WidgetsId: workspaceResponse.Info.WidgetsId,
|
||||
SpaceViewId: workspaceResponse.Info.SpaceViewId,
|
||||
TechSpaceId: workspaceResponse.Info.TechSpaceId,
|
||||
GatewayUrl: workspaceResponse.Info.GatewayUrl,
|
||||
LocalStoragePath: workspaceResponse.Info.LocalStoragePath,
|
||||
Timezone: workspaceResponse.Info.TimeZone,
|
||||
AnalyticsId: workspaceResponse.Info.AnalyticsId,
|
||||
NetworkId: workspaceResponse.Info.NetworkId,
|
||||
}, nil
|
||||
}
|
312
cmd/api/space/service_test.go
Normal file
312
cmd/api/space/service_test.go
Normal file
|
@ -0,0 +1,312 @@
|
|||
package space
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/gogo/protobuf/types"
|
||||
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service/mock_service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
)
|
||||
|
||||
const (
|
||||
offset = 0
|
||||
limit = 100
|
||||
techSpaceId = "tech-space-id"
|
||||
gatewayUrl = "http://localhost:31006"
|
||||
iconImage = "bafyreialsgoyflf3etjm3parzurivyaukzivwortf32b4twnlwpwocsrri"
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
*SpaceService
|
||||
mwMock *mock_service.MockClientCommandsServer
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
mw := mock_service.NewMockClientCommandsServer(t)
|
||||
spaceService := &SpaceService{mw: mw, AccountInfo: &model.AccountInfo{TechSpaceId: techSpaceId, GatewayUrl: gatewayUrl}}
|
||||
|
||||
return &fixture{
|
||||
SpaceService: spaceService,
|
||||
mwMock: mw,
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpaceService_ListSpaces(t *testing.T) {
|
||||
t.Run("successful retrieval of spaces", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: techSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_spaceView)),
|
||||
},
|
||||
{
|
||||
RelationKey: bundle.RelationKeySpaceLocalStatus.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.SpaceStatus_Ok)),
|
||||
},
|
||||
{
|
||||
RelationKey: bundle.RelationKeySpaceRemoteStatus.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.SpaceStatus_Ok)),
|
||||
},
|
||||
},
|
||||
Sorts: []*model.BlockContentDataviewSort{
|
||||
{
|
||||
RelationKey: "spaceOrder",
|
||||
Type: model.BlockContentDataviewSort_Asc,
|
||||
NoCollate: true,
|
||||
EmptyPlacement: model.BlockContentDataviewSort_End,
|
||||
},
|
||||
},
|
||||
Keys: []string{"targetSpaceId", "name", "iconEmoji", "iconImage"},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
"name": pbtypes.String("Another Workspace"),
|
||||
"targetSpaceId": pbtypes.String("another-space-id"),
|
||||
"iconEmoji": pbtypes.String(""),
|
||||
"iconImage": pbtypes.String(iconImage),
|
||||
},
|
||||
},
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
"name": pbtypes.String("My Workspace"),
|
||||
"targetSpaceId": pbtypes.String("my-space-id"),
|
||||
"iconEmoji": pbtypes.String("🚀"),
|
||||
"iconImage": pbtypes.String(""),
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
fx.mwMock.On("WorkspaceOpen", mock.Anything, mock.Anything).Return(&pb.RpcWorkspaceOpenResponse{
|
||||
Error: &pb.RpcWorkspaceOpenResponseError{Code: pb.RpcWorkspaceOpenResponseError_NULL},
|
||||
Info: &model.AccountInfo{
|
||||
HomeObjectId: "home-object-id",
|
||||
ArchiveObjectId: "archive-object-id",
|
||||
ProfileObjectId: "profile-object-id",
|
||||
MarketplaceWorkspaceId: "marketplace-workspace-id",
|
||||
WorkspaceObjectId: "workspace-object-id",
|
||||
DeviceId: "device-id",
|
||||
AccountSpaceId: "account-space-id",
|
||||
WidgetsId: "widgets-id",
|
||||
SpaceViewId: "space-view-id",
|
||||
TechSpaceId: "tech-space-id",
|
||||
GatewayUrl: "gateway-url",
|
||||
LocalStoragePath: "local-storage-path",
|
||||
TimeZone: "time-zone",
|
||||
AnalyticsId: "analytics-id",
|
||||
NetworkId: "network-id",
|
||||
},
|
||||
}, nil).Twice()
|
||||
|
||||
// when
|
||||
spaces, total, hasMore, err := fx.ListSpaces(nil, offset, limit)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Len(t, spaces, 2)
|
||||
require.Equal(t, "Another Workspace", spaces[0].Name)
|
||||
require.Equal(t, "another-space-id", spaces[0].Id)
|
||||
require.Regexpf(t, regexp.MustCompile(gatewayUrl+`/image/`+iconImage), spaces[0].Icon, "Icon URL does not match")
|
||||
require.Equal(t, "My Workspace", spaces[1].Name)
|
||||
require.Equal(t, "my-space-id", spaces[1].Id)
|
||||
require.Equal(t, "🚀", spaces[1].Icon)
|
||||
require.Equal(t, 2, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
|
||||
t.Run("no spaces found", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, mock.Anything).
|
||||
Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// when
|
||||
spaces, total, hasMore, err := fx.ListSpaces(nil, offset, limit)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrNoSpacesFound)
|
||||
require.Len(t, spaces, 0)
|
||||
require.Equal(t, 0, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
|
||||
t.Run("failed workspace open", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, mock.Anything).
|
||||
Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
"name": pbtypes.String("My Workspace"),
|
||||
"targetSpaceId": pbtypes.String("my-space-id"),
|
||||
"iconEmoji": pbtypes.String("🚀"),
|
||||
"iconImage": pbtypes.String(""),
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
fx.mwMock.On("WorkspaceOpen", mock.Anything, mock.Anything).
|
||||
Return(&pb.RpcWorkspaceOpenResponse{
|
||||
Error: &pb.RpcWorkspaceOpenResponseError{Code: pb.RpcWorkspaceOpenResponseError_UNKNOWN_ERROR},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
spaces, total, hasMore, err := fx.ListSpaces(nil, offset, limit)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrFailedOpenWorkspace)
|
||||
require.Len(t, spaces, 0)
|
||||
require.Equal(t, 0, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpaceService_CreateSpace(t *testing.T) {
|
||||
t.Run("successful create space", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
fx.mwMock.On("WorkspaceCreate", mock.Anything, mock.Anything).
|
||||
Return(&pb.RpcWorkspaceCreateResponse{
|
||||
Error: &pb.RpcWorkspaceCreateResponseError{Code: pb.RpcWorkspaceCreateResponseError_NULL},
|
||||
SpaceId: "new-space-id",
|
||||
}).Once()
|
||||
|
||||
fx.mwMock.On("WorkspaceOpen", mock.Anything, mock.Anything).Return(&pb.RpcWorkspaceOpenResponse{
|
||||
Error: &pb.RpcWorkspaceOpenResponseError{Code: pb.RpcWorkspaceOpenResponseError_NULL},
|
||||
Info: &model.AccountInfo{
|
||||
HomeObjectId: "home-object-id",
|
||||
ArchiveObjectId: "archive-object-id",
|
||||
ProfileObjectId: "profile-object-id",
|
||||
MarketplaceWorkspaceId: "marketplace-workspace-id",
|
||||
WorkspaceObjectId: "workspace-object-id",
|
||||
DeviceId: "device-id",
|
||||
AccountSpaceId: "account-space-id",
|
||||
WidgetsId: "widgets-id",
|
||||
SpaceViewId: "space-view-id",
|
||||
TechSpaceId: "tech-space-id",
|
||||
GatewayUrl: "gateway-url",
|
||||
LocalStoragePath: "local-storage-path",
|
||||
TimeZone: "time-zone",
|
||||
AnalyticsId: "analytics-id",
|
||||
NetworkId: "network-id",
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
space, err := fx.CreateSpace(nil, "New Space")
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "new-space-id", space.Id)
|
||||
})
|
||||
|
||||
t.Run("failed workspace creation", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
fx.mwMock.On("WorkspaceCreate", mock.Anything, mock.Anything).
|
||||
Return(&pb.RpcWorkspaceCreateResponse{
|
||||
Error: &pb.RpcWorkspaceCreateResponseError{Code: pb.RpcWorkspaceCreateResponseError_UNKNOWN_ERROR},
|
||||
}).Once()
|
||||
|
||||
// when
|
||||
space, err := fx.CreateSpace(nil, "New Space")
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrFailedCreateSpace)
|
||||
require.Equal(t, Space{}, space)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpaceService_ListMembers(t *testing.T) {
|
||||
t.Run("successfully get members", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, mock.Anything).
|
||||
Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
"id": pbtypes.String("member-1"),
|
||||
"name": pbtypes.String("John Doe"),
|
||||
"iconEmoji": pbtypes.String("👤"),
|
||||
"identity": pbtypes.String("AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ"),
|
||||
"globalName": pbtypes.String("john.any"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
"id": pbtypes.String("member-2"),
|
||||
"name": pbtypes.String("Jane Doe"),
|
||||
"iconImage": pbtypes.String(iconImage),
|
||||
"identity": pbtypes.String("AAjLbEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMD4"),
|
||||
"globalName": pbtypes.String("jane.any"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// when
|
||||
members, total, hasMore, err := fx.ListMembers(nil, "space-id", offset, limit)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Len(t, members, 2)
|
||||
require.Equal(t, "member-1", members[0].Id)
|
||||
require.Equal(t, "John Doe", members[0].Name)
|
||||
require.Equal(t, "👤", members[0].Icon)
|
||||
require.Equal(t, "john.any", members[0].GlobalName)
|
||||
require.Equal(t, "member-2", members[1].Id)
|
||||
require.Equal(t, "Jane Doe", members[1].Name)
|
||||
require.Regexpf(t, regexp.MustCompile(gatewayUrl+`/image/`+iconImage), members[1].Icon, "Icon URL does not match")
|
||||
require.Equal(t, "jane.any", members[1].GlobalName)
|
||||
require.Equal(t, 2, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
|
||||
t.Run("no members found", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, mock.Anything).
|
||||
Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// when
|
||||
members, total, hasMore, err := fx.ListMembers(nil, "space-id", offset, limit)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrNoMembersFound)
|
||||
require.Len(t, members, 0)
|
||||
require.Equal(t, 0, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
}
|
29
cmd/api/utils/utils.go
Normal file
29
cmd/api/utils/utils.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
)
|
||||
|
||||
// GetIconFromEmojiOrImage returns the icon to use for the object, which can be either an emoji or an image url
|
||||
func GetIconFromEmojiOrImage(accountInfo *model.AccountInfo, iconEmoji string, iconImage string) string {
|
||||
if iconEmoji != "" {
|
||||
return iconEmoji
|
||||
}
|
||||
|
||||
if iconImage != "" {
|
||||
return GetGatewayURLForMedia(accountInfo, iconImage, true)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetGatewayURLForMedia returns the URL of file gateway for the media object with the given ID
|
||||
func GetGatewayURLForMedia(accountInfo *model.AccountInfo, objectId string, isIcon bool) string {
|
||||
widthParam := ""
|
||||
if isIcon {
|
||||
widthParam = "?width=100"
|
||||
}
|
||||
return fmt.Sprintf("%s/image/%s%s", accountInfo.GatewayUrl, objectId, widthParam)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue