Skip to content

Commit

Permalink
GSB API OpenAPI (#137)
Browse files Browse the repository at this point in the history
* GSB API OpenAPI and AsyncAPI.

* GSB API docs with API usage sequence diagrams

---------

Co-authored-by: Przemyslaw Walski <pwalski@users.noreply.github.com>
Co-authored-by: stranger80 <stranger80@users.noreply.github.com>
  • Loading branch information
3 people authored Mar 9, 2023
1 parent 8bc7dab commit 909ace3
Show file tree
Hide file tree
Showing 4 changed files with 410 additions and 2 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
## Yagna public API
Public Yagna REST APIs client binding with Data Model and specifications in [OpenAPI](http://spec.openapis.org/) format.
# Yagna public API

Public Yagna REST APIs client binding with Data Model and specifications in
[OpenAPI](http://spec.openapis.org/) format.

## GSB API

GSB API documentation [page](docs/gsb_api.md).
138 changes: 138 additions & 0 deletions docs/gsb_api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# GSB API

GSB REST API allows to bind and unbind GSB services.

Binding GSB service enables WebSocket endpoint which allows to listen on incoming GSB messages.
Path to WebSocket endpoint is returned in bind service response.

API documentation:

- REST endpoints OpenAPI [schema](../specs/gsb-api.yaml).

- WS AsyncAPI [schema](../specs/gsb-api-messages.yaml).

Open it directly or by using AsyncAPI Studio.

`docker run -it -p 8000:80 asyncapi/studio`

## Sample GSB API usage scenarios

### Basic scenario

```mermaid
sequenceDiagram
participant App as WebSocket Client
participant Backend as Backend Yagna
participant Service as Golem Service
App ->>+ Backend: POST /gsb-api/v1/services:<br> { listen: { on: '/public/gftp/myapp', <br> components: ['GetMetadata', 'GetChunk'] }}
Backend -->> App: HTTP 201<br>{ listen: { ..., links: { messages: <br>'gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw' } } }
App ->> Backend: GET gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw <br> Headers: { Upgrade: websocket }
Backend -->> App: HTTP 101
par WebSocket connection lifespan
App ->> Backend: WS Event: Open
loop Multiple GSB message calls
Service ->>+ Backend: GSB: GetChunk <br>public/gftp/myapp
Backend ->> App: WS Message: gsbRequest<br> { id: '<br>L3B1YmxpYy9nZnRwL215YXBw', <br>component: 'GetMetadata', <br>payload: { .. } }
App -->> Backend: WS Message: gsbResponse<br> { id: '<br>L3B1YmxpYy9nZnRwL215YXBw', payload: { .. } }
Backend -->>- Service : GSB: GftpMetadata
end
App ->> Backend: DELETE /gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw
Backend -->> App: WS Event: Close<br> CloseCode: Normal
end
Backend -->>- App: HTTP 200
```

### Early GSB request scenario (simplified)

```mermaid
sequenceDiagram
participant App as WebSocket Client
participant Backend as Backend Yagna
participant Service as Golem Service
App ->>+ Backend: POST /gsb-api/v1/services
Backend -->> App: HTTP 201
Service ->>+ Backend: GSB: GetChunk
Note right of Backend: Buffered GSB request
App ->> Backend: GET gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw
Backend -->> App: HTTP 101
par WebSocket connection lifespan
App ->> Backend: WS Event: Open
Backend ->> App: WS Message: gsbRequest
App -->> Backend: WS Message: gsbResponse
Backend -->>- Service : GSB: GftpMetadata
App ->> Backend: DELETE /gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw
Backend -->> App: WS Event: Close<br> CloseCode: Normal
end
Backend -->>- App: HTTP 200
```

#### GSB messages buffering and cancelling (simplified)

```mermaid
sequenceDiagram
participant App as WebSocket Client
participant Backend as Backend Yagna
participant Service as Golem Service
App ->>+ Backend: POST /gsb-api/v1/services
Backend -->> App: HTTP 201
App ->> Backend: GET gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw
Backend -->> App: HTTP 101
par 1st WS connection lifespan
App -->> Backend: WS Event: Close<br>CloseCode: Abnormal
end
Service ->>+ Backend: GSB: GetMetadata
Note right of Backend: Buffering GSB request<br>when WS disconnected
App ->> Backend: GET gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw
Backend -->> App: HTTP 101
par 2nd WS connection lifespan
App ->> Backend: WS Event: Open
Backend ->> App: WS Message: gsbRequest
App -->> Backend: WS Message: gsbResponse
Backend -->>- Service : GSB: GftpMetadata
Service ->>+ Backend: GSB: GetChunk
Backend ->> App: WS Message: gsbRequest
App -->> Backend: WS Event: Close<br>CloseCode: Abnormal
end
Note right of Backend: Canceling GSB request<br> on WS Close
Backend -->>- Service: GSB: Error<br>ya_service_bus::Error::Closed
App ->> Backend: DELETE /gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw
Backend -->>- App: HTTP 200
```

### Disconnect WS on new WS connection (simplified)

```mermaid
sequenceDiagram
participant App0 as WebSocket Client<br>Old session
participant App1 as WebSocket Client<br>New session
participant Backend as Backend Yagna
App0 ->>+ Backend: POST /gsb-api/v1/services
Backend -->> App0: HTTP 201
App0 ->> Backend: GET gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw
Backend -->> App0: HTTP 101
par Old WS connection
App0 ->> Backend: WS Event: Open
App1 ->> Backend: GET gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw
Backend -->> App0: WS Event: Close<br>CloseCode: Policy
end
par New WS connection
Backend -->> App1: HTTP 101
App1 ->> Backend: WS Event: Open
App1 ->> Backend: DELETE /gsb-api/v1/services/<br>L3B1YmxpYy9nZnRwL215YXBw
Backend -->> App1: WS Event: Close<br> CloseCode: Normal
end
Backend -->>- App0: HTTP 200
```
118 changes: 118 additions & 0 deletions specs/gsb-api-messages.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
asyncapi: "2.6.0"

info:
title: GSB services Websocket API
description: "
GSB services Websocket API allows to listen and respond to incoming GSB messages.
In order to access endpoint required is to bind GSB services using 'POST /services' REST API endpoint.
Request to 'DELETE /services/{servicesId}' closes WebSocket connection, cancels pending GSB requests, and disables WebSocket endpoint created for these services.
"
version: "0.1.0"

servers:
public:
url: /gsb-api/v1
protocol: ws

channels:
/services/{servicesId}:
parameters:
servicesId:
description: "
Generated id for bound GSB services.
Path with id is in response of POST /services (field 'listen.links.messages')
"
schema:
type: string
bindings:
ws:
method: GET
subscribe:
summary: GSB requests
description: GSB message requests to bound services
message:
$ref: "#/components/messages/gsbRequest"

publish:
summary: GSB responses
description: GSB message responses
message:
$ref: "#/components/messages/gsbResponse"

components:
messages:
gsbRequest:
payload:
$ref: "#/components/schemas/gsbRequest"
contentType: application/octet-stream
gsbResponse:
payload:
$ref: "#/components/schemas/gsbResponse"
contentType: application/octet-stream

schemas:
gsbRequest:
summary: "GSB API services message request"
type: object
required:
- "id"
- "component"
properties:
id:
$ref: "#/components/schemas/id"
component:
type: string
description: "GSB message REQUEST type name"
examples:
- "GetChunk"
payload:
type: object
description: "
Serialized into object GSB message of 'component' REQUEST type (e.g. GetChunk type).
Some GSB messages can be found here https://github.com/golemfactory/yagna/tree/master/core/model/src
In case of request with no payload it can be empty (or missing).
"
contentMediaType: application/octet-stream
contentEncoding: flexbuffers

gsbResponse:
summary: "GSB API services message response"
type: object
oneOf:
- required:
- id
- payload
not:
required:
- error
- required:
- id
- error
not:
required:
- payload
properties:
id:
$ref: "#/components/schemas/id"
payload:
type: object
description: "
Serialized into object GSB message of 'component' RESPONSE type
(e.g. for GetChunk it will have GftpChunk type).
Some GSB messages can be found here https://github.com/golemfactory/yagna/tree/master/core/model/src
"
error:
type: object
description: "
Object mapping GSB message error variant name to error message value.
Example shows error ya_core_model::gftp::Error::InternalError.
Some GSB message errors types can be found here https://github.com/golemfactory/yagna/tree/master/core/model/src
"
examples:
- { "InternalError": "Failed to read file" }
contentMediaType: application/octet-stream
contentEncoding: flexbuffers

id:
type: string
description: Id used to match request to response
Loading

0 comments on commit 909ace3

Please sign in to comment.