Skip to content

Commit

Permalink
Merge pull request #52 from ba-st/docs
Browse files Browse the repository at this point in the history
Update documentation
  • Loading branch information
gcotelli authored Dec 1, 2021
2 parents 65b7d10 + a30c681 commit 8537a4e
Show file tree
Hide file tree
Showing 11 changed files with 428 additions and 90 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ Quick links

## Installation

To load the project in a Pharo image, or declare it as a dependency of your own
project follow this [instructions](docs/Installation.md).
To load the project in a Pharo image follow these [instructions](docs/how-to/how-to-load-in-pharo.md).

## Contributing

Expand Down
60 changes: 0 additions & 60 deletions docs/Installation.md

This file was deleted.

22 changes: 22 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Superluminal Documentation

Superluminal provides building blocks for creating HTTP requests and API
clients, including:

- Entity tags handling
- Caching
- Service Discovery

To learn about the project, [install it](how-to/how-to-load-in-pharo.md) and
lookup for details in the reference docs:

- [HTTP Requests](reference/HTTP-Requests.md)
- [API Clients](reference/API-Client.md)
- [Service Discovery](reference/Service-Discovery.md)

---

To use the project as a dependency of your project, take a look at:

- [How to use Superluminal as a dependency](how-to/how-to-use-as-dependency-in-pharo.md)
- [Baseline groups reference](reference/Baseline-groups.md)
35 changes: 35 additions & 0 deletions docs/how-to/how-to-load-in-pharo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# How to load Superluminal in a Pharo image

## Using Metacello

1. Download a [Pharo VM and image](https://pharo.org/download)
2. Open your Pharo image
3. Open a Playground
4. Evaluate:

```smalltalk
Metacello new
baseline: 'Superluminal';
repository: 'github://ba-st/Superluminal:release-candidate';
load: 'Development'.
```
> Change `release-candidate` to some released version if you want a pinned version
## Using Iceberg
1. Download [pharo VM and image](https://pharo.org/download)
2. Open your Pharo image
3. Open Iceberg
4. Click the *Add* repository button
5. Select *Clone from github.com* and enter `ba-st` as owner name and `Superluminal`
as project name
6. Click *Ok*
7. Select the repository in the main Iceberg window
8. Open the contextual menu and select
*Metacello -> Install baseline of Superluminal ...*
9. Type `Development` and click *Ok*
> After Iceberg cloned a repository, it will be checked-out at the default
> branch (in this case `release-candidate`). If you want to work on a different
> branch or commit perform the checkout before the baseline installation step.
42 changes: 42 additions & 0 deletions docs/how-to/how-to-use-as-dependency-in-pharo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# How to use Superluminal as dependency in a Pharo product

In order to include **Superluminal** as part of your project, you should reference
the package in your product baseline:

1. Define the Superluminal repository and version to be used, and the [baseline groups](../reference/Baseline-groups.md)
you want to depend on (usually `Deployment`).

If you're unsure about what to depend on use the *Dependency Analyzer*
tool to choose an appropriate group including the packages you need.

2. Create a method like this one in the baseline class of your product:

```smalltalk
setUpDependencies: spec
spec
baseline: 'Superluminal'
with: [ spec repository: 'github://github://ba-st/Superluminal:v{XX}' ];
project: 'Superluminal-Deployment'
copyFrom: 'Superluminal' with: [ spec loads: 'Deployment' ]
```
This will create `Superluminal-Deployment` as a valid target that can be used
as requirement in your own packages.
> Replace `{XX}` with the version you want to depend on
3. Use the new loading target as a requirement on your packages. For example:
```smalltalk
baseline: spec
<baseline>
spec
for: #pharo
do: [
self setUpDependencies: spec.
spec
package: 'My-Package'
with: [ spec requires: #('Superluminal-Deployment') ] ]
```
140 changes: 140 additions & 0 deletions docs/reference/API-Client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# API Client

When dealing with HTTP APIs, the `API Client` group in the baseline provides
abstractions to ease this kind of interaction.

`RESTfulAPIClient` is the key abstraction for these features. Some of its
functionality is more useful when dealing with a RESTful API but is usable for
non-RESTful APIs as well.

`RESTfulAPIClient` provides pooling of the underlying HTTP client, handling of
entity tags, and caching support.

To create an API client you need to provide a block for creating a basic HTTP
client instance and the caching policy.

For example:

```smalltalk
RESTfulAPIClient
buildingHttpClientWith: [ ZnClient new ]
cachingIn: ExpiringCache onLocalMemory
```

will instantiate a client pooling ZnClient instances and using an in-memory cache.

API clients support the following methods:

- `getAt:configuredBy:withSuccessfulResponseDo:` will execute a GET against the
location in the first argument, configured by the second argument. If the
response is successful the last argument is evaluated with the response's body.
- `postAt:configuredBy:withSuccessfulResponseDo:` will execute a POST against the
location in the first argument, configured by the second argument. If the
response is successful the last argument is evaluated with the response's body.
- `putAt:configuredBy:withSuccessfulResponseDo:` will execute a PUT against the
location in the first argument, configured by the second argument. If
the response is successful, the last argument is evaluated with the response's
body, unless it is `204/No content`.
- `patchAt:configuredBy:withSuccessfulResponseDo:` will execute a PATCH against the
location in the first argument, configured by the second argument. If
the response is successful, the last argument is evaluated with the response's
body.
- `deleteAt:configuredBy:withSuccessfulResponseDo:` will execute a DELETE against
the location in the first argument, configured by the second argument. If the
response is successful the last argument is evaluated with the response's body.

The configuration blocks follow the builder pattern defined [here](HTTP-Request.md).

If the execution is unsuccessful an `HTTPClientError` exception is raised with
the corresponding response code.

It also supports some convenience methods built on the previous ones:

- `get:accepting:withSuccessfulResponseDo:` will execute a GET against the
location in the first argument, setting the `Accept` header according to the
second argument. If the response is successful the last argument is evaluated
with the response's body.
- `get:withSuccessfulResponseDo:` will execute a GET against the location in the
first argument without further configuration. If the response is successful
the last argument is evaluated with the response's body.
- `post:at:withSuccessfulResponseDo:` will execute a POST, whose body and
`Content-Type` is defined by the entity in the first argument, against the
second argument. If the response is successful the last argument is evaluated
with the response's body.
- `patch:at:withSuccessfulResponseDo:` will execute a PATCH, whose body and
`Content-Type` is defined by the entity in the first argument, against the
second argument. If the response is successful the last argument is evaluated
with the response's body.
- `put:at:` will execute a PUT, whose body and `Content-Type` is defined by the
entity in the first argument, against the second argument. If the response is
successful the last argument is evaluated with the response's body.
- `deleteAt:` will execute a DELETE against the location in the first argument.
If there's an entity tag associated with this location it will set the
`If-Match` header making the delete conditional.

## Pooling

Every time an API call is made, if a new authority and port is used
for the invocation the API client will create a connection pool.

Currently, each pool created has at least one connection alive with
a maximum of 5 connections.

On API client disposition all the connections in the pool are closed.

## Entity tags handling

The `ETag` HTTP response header is an identifier for a specific version of a
resource. It allows caches to be more efficient, and saves bandwidth, as a web
server does not need to send a full response if the content has not changed. On
the other side, if the content has changed, entity tags are useful to help prevent
simultaneous updates of a resource from overwriting each other ("mid-air collisions").

If the resource at a given URL changes, a new entity tag value must be generated.
Entity tags are therefore similar to fingerprints and might also be used for tracking
purposes by some servers. A comparison of them allows us to quickly determine
whether two representations of a resource are the same, but they might also be
set to persist indefinitely by a tracking server.

`RESTfulAPIClient` takes advantage of entity tags, when present, in the following
scenarios:

- When receiving a successful response including the `ETag` header, both the
entity tag value and the response body are cached in the client.
- When using `deleteAt:` or `patch:at:withSuccessfulResponseDo:` convenience
methods, if an entity tag was previously saved its value is used in an
`If-Match` header preventing deleting or patching a resource whose representation
has changed from the last time it was seen by the client.
- When using any of the `GET`-related methods, if an entity tag was previously
saved its value is used in an `If-None-Match` header. Giving the server the
chance to respond with `204/Not modified` and in that case, the previously
saved body is used as the response body.

## Caching

`RESTfulAPIClient` also provides a resource cache. It can be configured to use an
in-memory cache (inside the same image) or a shared cache using [Memcached](https://www.memcached.org/).

To use an in-memory cache provide it to the API client doing:

```smalltalk
ExpiringCache onLocalMemory
```

and, for a shared cache:

```smalltalk
ExpiringCache onDistributedMemoryAt: serverList
```

where `serverList` is something like `{'127.0.0.1:11211'}`

Both kinds of caches take into account the `Cache-Control` headers received in
the responses. When the API client receives any of the `GET`-related messages it
looks up in the cache if there's a non-expired resource cached for this location.
If it is, it will reuse that. In case there's no cached resource or the cached one
has expired it will proceed to execute the `GET`.

Resources are cached when a successful `GET` response is received; and cleared when
any `POST`, `PUT`, `PATCH`, or `DELETE` method is executed against this location,
or when a cached resource is expired.
24 changes: 24 additions & 0 deletions docs/reference/Baseline-groups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Baseline Groups

Superluminal includes the following groups in its Baseline that can be used as
loading targets:

- `Core` will load the minimal required packages to support `HttpRequest` and
its building interface
- `API Client` will load the packages needed to support API clients, including:
- HTTP client pooling by authority
- ETag caching and automatic support for `If-None-Match` and `If-Match` headers,
and `Not modified` responses
- Caching support according to `Cache-Control` headers, both in-memory or using
memcached
- `Service Discovery` will load the packages needed to support service discovery
against a Consul agent
- `Deployment` will load all the packages needed in a deployed application, which
in this case correspond to `Core` + `API Client` + `Service Discovery`
- `Examples` will load a service discovery-enabled application
- `Tests` will load the test cases
- `Dependent-SUnit-Extensions` will load extensions to SUnit for testing API clients
- `CI` is the group loaded in the continuous integration setup, in this
particular case it is the same as `Tests`
- `Development` will load all the needed packages to develop and contribute to
the project
Loading

0 comments on commit 8537a4e

Please sign in to comment.