Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ name: deploy-docs

on:
workflow_dispatch:
repository_dispatch:
types: [trigger-downstream]
# repository_dispatch:
# types: [trigger-downstream]

env:
REGISTRY: ghcr.io
Expand Down
6 changes: 0 additions & 6 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
[submodule "deployment/helm/cert-issuer-helm-chart"]
path = deployment/helm/cert-issuer-helm-chart
url = https://gitlab.com/hololinked/kubernetes/helm-charts/cert-issuer-helm-chart.git
[submodule "deployment/helm/container-helm-chart"]
path = deployment/helm/container-helm-chart
url = https://gitlab.com/hololinked/kubernetes/helm-charts/container-helm-chart.git
[submodule "deployment/helm/ingress-helm-chart"]
path = deployment/helm/ingress-helm-chart
url = https://gitlab.com/hololinked/kubernetes/helm-charts/ingress-helm-chart.git
1 change: 0 additions & 1 deletion deployment/helm/cert-issuer-helm-chart
Submodule cert-issuer-helm-chart deleted from fbcbd1
1 change: 0 additions & 1 deletion deployment/helm/ingress-helm-chart
Submodule ingress-helm-chart deleted from 343aff
2 changes: 1 addition & 1 deletion docs/api-reference/clients/bases/utils.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@


::: hololinked.client.abstractions.raise_local_exception
<!-- ::: hololinked.client.abstractions.raise_local_exception -->
8 changes: 8 additions & 0 deletions docs/api-reference/clients/security/apikey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@


::: hololinked.client.security.APIKeySecurity
options:
members:
- __init__
- value
- http_header_name
7 changes: 7 additions & 0 deletions docs/api-reference/clients/security/basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


::: hololinked.client.security.BasicSecurity
options:
members:
- __init__
- http_header
6 changes: 6 additions & 0 deletions docs/api-reference/clients/security/oidc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

<!-- ::: hololinked.client.security.OAuthDirectAccessGrantSecurity
options:
members:
- __init__
- http_header -->
3 changes: 3 additions & 0 deletions docs/api-reference/td/form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@


::: hololinked.td.forms.Form
5 changes: 5 additions & 0 deletions docs/api-reference/td/metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@


::: hololinked.td.metadata.Link

::: hololinked.td.metadata.VersionInfo
11 changes: 11 additions & 0 deletions docs/api-reference/td/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@


::: hololinked.td.security_definitions.SecurityScheme

::: hololinked.td.security_definitions.NoSecurityScheme

::: hololinked.td.security_definitions.BasicSecurityScheme

::: hololinked.td.security_definitions.APIKeySecurityScheme

::: hololinked.td.security_definitions.OIDCSecurityScheme
70 changes: 34 additions & 36 deletions docs/beginners-guide/articles/object-proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,28 @@

[API Reference](../../api-reference/clients/object-proxy.md)

`Thing` objects can be consumed using an `ObjectProxy` instance, per protocol, where the interactions with
a property, action or event can be abstracted as operations like:
`ObjectProxy` is a procedural client meant to consume a `Thing` instance where the interactions with a property, action or event can be abstracted as operations like:

- Read/Write/Observe Property
- Invoke Action
- Subscribe/Unsubscribe Event

Further, one would require a [Thing Description](https://www.w3.org/TR/wot-thing-description11/#introduction-td) to construct the client.
The `Thing Description` contains the metadata of the `Thing` like available properties, actions and events, their data types,
forms (protocols and endpoints) etc. which can be used to create the `ObjectProxy`. In `hololinked`, the `Thing Description` is
automatically generated and served by the server protocols, and there is lesser requirement for manual intervention.
One would require a [Thing Description](https://www.w3.org/TR/wot-thing-description11/#introduction-td) to construct the client. The `ThingDescription` contains the metadata of the `Thing` like available properties, actions and events, their data types, their protocols and endpoints (called `forms`), among other metadata, in JSON format.

To instantiate an `ObjectProxy`, use the `ClientFactory`:
!!! Info

See some online hosted examples at the [examples website](https://examples.hololinked.dev) -
[Camera](https://examples.hololinked.dev/simulations/camera/resources/wot-td?ignore_errors=true) |
[Spectrometer](https://examples.hololinked.dev/simulations/spectrometer/resources/wot-td) |
[Oscilloscope](https://examples.hololinked.dev/simulations/oscilloscope/resources/wot-td) <br/>

The purpose of this JSON metadata is to provide both a human and machine readable description of the `Thing` and its capabilities, so that clients can automatically discover and interact with it without prior knowledge. In `hololinked`, this JSON document is automatically generated and served by the server protocols. There is lesser requirement for manually creating said `ThingDescription`, unless one wants to highly customize it.

To instantiate an `ObjectProxy`, use the `ClientFactory` for one protocol at a time:

!!! Note

Only one protocol is allowed per `ObjectProxy` client. You can always create multiple clients if you need multiple protocols.

=== "HTTP"

Expand All @@ -23,8 +32,8 @@ To instantiate an `ObjectProxy`, use the `ClientFactory`:

thing = ClientFactory.http(url="http://localhost:8000/my-thing/resources/wot-td")
```
One needs to append `/resources/wot-td` to the URL to load a `Thing Description`, the reason being that if one
stores pregenerated Thing Descriptions in a different location, one can still load them.
For HTTP, one needs to append `/resources/wot-td` to the URL to load an automatically generated `Thing Description`
from the HTTP server serving the `Thing`.

=== "ZMQ"

Expand All @@ -50,11 +59,11 @@ To instantiate an `ObjectProxy`, use the `ClientFactory`:
)
```

When using ZMQ-TCP, on the server side one may specify the address as `access_point="tcp://*:5555"`.
On the client side, however, one must use the explicit address, like `access_point="tcp://my-raspberry-pi:5555"` or
`access_point="tcp://localhost:5555"`.
For ZMQ, one needs to specify the `server_id`, `thing_id` and the `access_point` (say, `TCP` or `IPC`) where the server is accessible. These values are customizable while instantiating an instance of the [`ZMQServer`](../articles/protocols/general.md). If the `run()` method on the `Thing` instance was used, the `server_id` defaults to `thing_id`.

When using ZMQ-TCP, on the server side one may specify the address as `access_point="tcp://*:5555"` to bind on all interfaces. On the client side, however, one must use the explicit address containing the machine hostname, like `access_point="tcp://my-raspberry-pi:5555"` or `access_point="tcp://localhost:5555"`.

The `Thing Description` is fetched automatically from the server for ZMQ transport.
The `Thing Description` is fetched automatically from the server while mediating the connection.

=== "MQTT"

Expand All @@ -69,14 +78,10 @@ To instantiate an `ObjectProxy`, use the `ClientFactory`:
)
```

MQTT usually supports only pub-sub or event based interactions. Therefore, only event subscriptions
are supported on the `ObjectProxy` and properties and actions raise `AttributeError`.
On subscription, the broker should publish a `Thing Description` to the topic `<thing_id>/thing-description`
so that the client can find other available events.
The `Thing Description` is published to the MQTT Broker under the topic `<thing_id>/thing-description` by the server,
and the `ClientFactory` subsribes to the `Thing Description` and constructs the `ObjectProxy`.

!!! Note

Only one protocol is allowed per client.
MQTT currently supports only events and properties that publish change events.

### read and write properties

Expand Down Expand Up @@ -108,23 +113,19 @@ One can also use `invoke_action` to invoke an action by name

### oneway scheduling

`oneway` scheduling do not fetch return value and exceptions that might occur while executing a property or an action.
The server schedules the operation and returns an empty response to the client, allowing it to process further logic.
It is possible to set a property, set multiple or all properties or invoke an action in
oneway. Other operations are not supported.
`oneway` scheduling do not fetch return value and exceptions that might occur while executing a property or an action. The server schedules the operation and returns an empty response to the client, allowing it to process further logic. It is possible to set a property, set multiple or all properties or invoke an action in oneway. Other operations are not supported.

```py title="oneway=True" linenums="1"
--8<-- "docs/beginners-guide/code/object_proxy/sync.py:79:103"
```

Simply provide the keyword argument `oneway=True` to the operation method.

Importantly, one cannot have an action argument or a property on the server named `oneway` as it is a
reserved keyword argument to such methods on the client. At least they become inaccessible on the `ObjectProxy`.
`oneway` must be always specified as a keyword argument. Due to this reason, one cannot have an action argument or a property on the server named `oneway` as it is a reserved keyword argument to such methods on the client. At least they become inaccessible on the `ObjectProxy`.

### no-block scheduling

`noblock` allows scheduling a property or action but collecting the reply later:
`noblock` allows scheduling a property or action and collecting the reply later:

```py title="noblock=True" linenums="1"
--8<-- "docs/beginners-guide/code/object_proxy/sync.py:107:139"
Expand Down Expand Up @@ -156,7 +157,7 @@ Simply prefix `async_` to the method name, like `async_read_property`, `async_wr
There is no support for dot operator based access for asyncio. One may also note that `async` operations
do not change the nature of the execution on the server side.
`asyncio` on `ObjectProxy` is purely a client-side non-blocking network call, so that one can
simultaneously perform other async operations while the client is waiting for the network operation to complete.
simultaneously perform other async operations while the client is waiting for said network operation to complete.

!!! Note

Expand Down Expand Up @@ -214,15 +215,14 @@ Once again, to customize callback scheduling, see [events section](./events.md#s

##### foreign attributes on client

Normally, there cannot be user defined attributes on the `ObjectProxy` as the attributes on the client
must mimic the available properties, actions and events on the server. An accidental setting of an unknown
property must raise an `AttributeError` when not found on the server, instead of silently going through and setting
said property on the client object itself:
Normally, there cannot be user defined attributes on the `ObjectProxy` as the attributes on the client must mimic the available properties, actions and events on the server. An accidental setting of an unknown property must raise an `AttributeError`, when not found on the server, instead of silently setting said property on the client itself:

```py title="foreign attributes raise AttributeError" linenums="1"
--8<-- "docs/beginners-guide/code/object_proxy/customizations.py:3:7"
```

The requirement for this behaviour is due to python's duck typing. If one intends to set a property named `foo`, and instead types it as `fooo` (misspelt), it is better to raise an error instead of silently setting a new attribute `fooo` on the client.

One can overcome this by setting `allow_foreign_attributes` to `True`:

```py title="foreign attributes allowed" linenums="1"
Expand All @@ -236,15 +236,13 @@ For invoking any operation (say property read/write & action call), two types of
- `invokation_timeout` - the amount of time the server has to wait for an operation to be scheduled
- `execution_timeout` - the amount of time the server has to complete the operation once scheduled

When the `invokation_timeout` expires, the operation is guaranteed to be never scheduled. When the `execution_timeout` expires, the operation is scheduled but returns without the expected response. In both cases, a `TimeoutError` is raised on the client side specifying the timeout type. If an operation is scheduled but not completed within the `execution_timeout`, the server may still complete the operation and there can be unknown side effects or client does not know about it.
When the `invokation_timeout` expires, the operation is guaranteed to be never executed. When the `execution_timeout` expires, the operation is scheduled but returns without the expected response. In both cases, a `TimeoutError` is raised on the client specifying the timeout type. If an operation is scheduled but not completed within the `execution_timeout`, the server may still complete the operation but client does not know about it.

```py title="timeout specification" linenums="1"
--8<-- "docs/beginners-guide/code/object_proxy/customizations.py:29:36"
```

!!! Note

Currently only a global specification is supported. In future, one may be able to specify timeouts per operation.
> Currently only a global customization of these values are supported. In future, one may be able to specify timeouts per operation.

<!-- #### change handshake timeout

Expand Down
47 changes: 30 additions & 17 deletions docs/beginners-guide/articles/protocols/general.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
# General

There are multiple ways to start serving your `Thing`:
There are multiple ways to start serving one's `Thing` instance based on the application requirements:

- One needs to run one `Thing` in multiple protocols
- One needs to run multiple `Thing`s in one protocol
- One needs to run multiple `Thing`s in multiple protocols
- One needs to run one `Thing` instance in one or multiple protocols
- One needs to run multiple `Thing` instances in one protocol
- One needs to run multiple `Thing`s instances in multiple protocols

## Run Single Thing in Multiple Protocols

There are two possible syntax options:
To configure a protocol server in a detailed fashion, instantiate them explicitly.
To start serving, one can pass them as an argument to the `run()` method of the `Thing`:

```python linenums="1"
from hololinked.server import HTTPServer, MQTTServer
```python linenums="1" title="Multiple Protocol Servers"
from hololinked.server import HTTPServer, MQTTPublisher, ZMQServer

http_server = HTTPServer(port=9000)
mqtt_server = MQTTServer(host='mqtt.example.com', port=1883)
zmq_server = ZMQServer(access_points=['IPC', 'tcp://*:9001'])
mqtt_server = MQTTPublisher(host='mqtt.example.com')
zmq_server = ZMQServer(id='oscilloscope-server', access_points=['IPC', 'tcp://*:9001'])

Oscilloscope(id='oscilloscope').run(servers=[http_server, mqtt_server, zmq_server])
```

OR
Based on the protocol, the servers may support overriding the exposed configuration, for example, changing the HTTP URL path, or overriding an MQTT publishing worker.

```python linenums="1"
from hololinked.server import HTTPServer, MQTTServer
> The number of such features supported is generally a work in progress. Please consider reading the codebase if you wish to implement a highly specific feature, and also consider contributing your feature to the repository - [How to Contribute](https://docs.hololinked.dev/introduction/contributing/).

Alternatively, to quickly expose the `Thing` instance, supply the protocol name and the access point (port or address) as a tuple to the `run()` method.

```python linenums="1" title="Quick Start"
Oscilloscope(id='oscilloscope').run(
access_points=(
('HTTP', 9000),
Expand All @@ -34,11 +37,18 @@ Oscilloscope(id='oscilloscope').run(
)
```

The first option is obviously preferred.
HTTP and ZMQ support a `run_with_<protocol>` method:

```python linenums="1" title="Quick Start Option 2"
Oscilloscope(id='oscilloscope').run_with_http_server(port=9000, ssl_context=ssl_context)
```

## Run Multiple Things in One Protocol

```python linenums="1"
All protocols support an `add_thing()` method that accepts a `Thing` instance
and a `run()` and `stop()` method that control their boot up & shutdown:

```python linenums="1" title="add Things to protocol server"
from hololinked.server import HTTPServer

server = HTTPServer(port=9000)
Expand All @@ -50,14 +60,17 @@ server.run()

## Run Multiple Things in Multiple Protocols

```python linenums="1"
from hololinked.server import HTTPServer, MQTTServer, run
Use the global `run()` method along with `server.add_thing()` to start any number of `Thing` instances in any number of protocols:

```python linenums="1" title="Run Multiple Things in Multiple Protocols"
from hololinked.server import HTTPServer, MQTTPublisher, run

http_server = HTTPServer(port=9000)
mqtt_server = MQTTServer(host='mqtt.example.com', port=1883)
mqtt_server = MQTTPublisher(host='mqtt.example.com')

http_server.add_thing(Oscilloscope(id='oscilloscope'))
mqtt_server.add_thing(DCPowerSupply(id='dc-power-supply'))

run(servers=[http_server, mqtt_server])
# HTTP server serves Oscilloscope and MQTTPublisher publishes events from DCPowerSupply
```
Loading