Newer
Older
web-services / README.md
@François Parmentier François Parmentier on 22 Feb 9 KB docs(README): Fix typos

# Les web services TDM de l'Inist

## Web services en Python

Créer un web service en Python consiste à créer un script Python capable de
traiter un fichier JSON fourni via l'entrée standard (sdtin) et de produire un
fichier JSON similaire dans la sortie standard (stdout).

### Exemple de fichier python

```python
#!/usr/bin/python3
import sys
import json

for line in sys.stdin:
   data = json.loads(line)
   data['value'] = data['value'].upper() # YOUR STATEMENT HERE
   sys.stdout.write(json.dumps(data))
   sys.stdout.write('\n')
```

Pour transformer un programme Python en web service, il est nécessaire de
déclarer un point d’entrée (entrypoint) via la création d’un fichier `.ini` dans
une arborescence spécifique (cf . [Convention de nommage](#convention-de-nommage)).

Ce fichier contiendra la documentation au format OpenAPI, et une référence au
fichier python a exécuter.

#### Exemple de fichier .ini

```ini
# OpenAPI Documentation - JSON format (dot notation)
mimeType = application/json

post.responses.default.description = Return all objects with enrich fields
post.responses.default.content.application/json.schema.$ref =  #/components/schemas/JSONStream
post.summary = Enrich one field of each Object with a Python function
post.requestBody.required = true
post.requestBody.content.application/json.schema.$ref = #/components/schemas/JSONStream
post.parameters.0.in = query
post.parameters.0.in = query
post.parameters.0.name = indent
post.parameters.0.schema.type = boolean
post.parameters.0.description = Indent or not the JSON Result

[use]
plugin = @ezs/local
plugin = @ezs/basics
plugin = @ezs/storage
plugin = @ezs/analytics

[JSONParse]
separator = *

[expand]
path = value
size = 100

[expand/exec]
# command should be executable !
# Use absolute path
command = ./v1/strings/uppercase.py

[dump]
indent = env('indent', false)
```

L'installation de paquet Python spécifique est possible en déclarant les paquets
à installer à la racine de son web service dans un ficher `requirements.txt`.

### Exemple de fichier requirements.txt

```txt
spacy==2.3.5
pytextrank==2.0.1
pycld3
en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.3.1/en_core_web_sm-2.3.1.tar.gz
fr-core-news-sm @ https://github.com/explosion/spacy-models/releases/download/fr_core_news_sm-2.3.0/fr_core_news_sm-2.3.0.tar.gz
```

## Développer

Il est possible de tester localement (sur son poste) son webservice, dans un
environnement identique à celui de production, pour cela :

### en Python

```bash
make python ./mon_repertoire
```

### en NodeJS

```bash
make nodejs ./base-line
# ou
cd base-line && ezs -v -d .

```

Ensuite, le webservice est accessible à cette adresse <http://localhost:31976>.

> **WARNING:**
> Docker modifie le propriétaire des fichiers. Pour restaurer les permissions:
>
> ```bash
> make reset
> ```

## Tester

Des exemples de requêtes sont disponibles dans des fichers `examples.http`.  
Ceux-ci peuvent être utilisés directement dans VSCode, avec l'extension REST Client (humao.rest-client).  
Ils peuvent également être lancés en ligne de commande via [rest-cli](https://www.npmjs.com/package/rest-cli)
ou via [dot-http](https://github.com/bayne/dot-http).

Exemple 1 : lancement des exemples sans affichage

```bash
$ restcli  ./biblio-tools/examples.http
examples:1 [1] POST https://biblio-tools.services.inist.fr/v1/unpaywall/is_oa?indent=true
examples:2 [1] POST https://biblio-tools.services.inist.fr/v1/crossref/prefixes/expand?indent=true
```

Exemple 2 : lancement des exemples avec affichage

```bash
$ restcli --full  ./affiliations-libpostal/examples.http
examples:1 [1] POST https://affiliations-libpostal.services.inist.fr/v1/parse?indent=true
POST https://affiliations-libpostal.services.inist.fr/v1/parse?indent=true
Content-Type: application/json

[
    {
        "value": "Barboncino 781 Franklin Ave, Crown Heights, Brooklyn, NY 11238"
    }
]

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Connection: close
Content-Disposition: inline
Content-Encoding: gzip
Content-Type: application/octet-stream
Date: Fri, 23 Jul 2021 09:00:14 GMT
Server: nginx/1.15.10
Transfer-Encoding: chunked

[{
    "value": {
        "id": "Barboncino 781 Franklin Ave, Crown Heights, Brooklyn, NY 11238",
        "value": {
            "house": "barboncino",
            "house_number": "781",
            "road": "franklin ave",
            "suburb": "crown heights",
            "city_district": "brooklyn",
            "state": "ny",
            "postcode": "11238"
        }
    }
}]
```

## Documenter

### Documentation OpenAPI

Chaque service, chaque route, doit être documenté en respectant la norme [OpenAPI](https://swagger.io/specification/) 3.0 (swagger).

La documentation de chaque route doit être placée dans le fichier `.ini` correspondant à sa route API.

Pour placer la document openapi dans les fichiers `.ini`, la syntaxe Swagger au format JSON doit être convertie au format "dot notation"

Exemple :

```ini
post.responses.default.description = Return a JSON as received JSON.
post.responses.default.content.application/json.schema.$ref = #/components/schemas/anyValue
post.summary = Parse and split JSON and return it.
post.requestBody.required = true
post.requestBody.content.application/json.schema.$ref = #/components/schemas/anyValue
```

Si vous souhaitez enrichir la documentation par défaut, en ajoutant des _tags_,
des components, il est possible d'ajouter un fichier standard `swagger.json` à
la racine de votre instance.  
Le serveur prendra en compte tout le fichier SAUF ce qui correspond aux
informations concernant les routes (qui doivent être placées dans le fichier
`.ini`).

### Déclarer la documentation Swagger

Le répertoire `www-home` contient le HTML de la page d'accueil
<https://services.inist.fr>.

Pour ajouter une instance dans le menu déroulant (en haut à droite), il vous
suffit de la déclarer dans `www-home/index.html` pour qu'elle soit prise en
compte.

## Convention de nommage

Chaque instance sur l'[ezmaster](https://github.com/Inist-CNRS/ezmaster) de la
machine aura un nom en trois parties:

1. texte (domaine)
2. texte (spécialité)
3. numéro (version)

La troisième partie du champ ne doit pas être communiquée à l'extérieur, c'est
un numéro de version (si plusieurs instances ont les mêmes deux premiers champs,
c'est la dernière version qui est exposée quand on n'utilise pas de numéro de
version, ce qui sera le cas).

Le numéro de version permet la mise à jour de l'image docker associée au webservice.

### Nom de l'instance

Les champs textuels sont obligatoirement en minuscules, et sans accent (mais
peuvent contenir des chiffres) et donc de préférence en anglais. Ils ne doivent
pas contenir un nom de personne.

La première partie décrit le **domaine** d'application du webservice.
Exemple : `affiliation`, `text`, `classification`, `nlp`, etc.

La seconde partie décrit une **spécialité** du domaine.
Cette spécificité est principalement liée à une image docker différente, comme
c'est le cas si les différents _web services_ d'un domaine utilisent des
langages différents (C++, python, nodejs, etc.).

### Nom des répertoires

Chaque instance donne accès à une arborescence de webservices.  
Le nom de route dépend du nom des répertoires.  
Les noms des répertoires doivent donc être choisis dans cette optique.

Il convient donc de respecter les pratiques communément admises dans la définition d'API de type REST.  
En utilisant des noms de répertoires en minuscules, sans accent (mais pouvant
contenir des chiffres) et de préférence en anglais.

Si le premier niveau caractérise obligatoirement la version, les suivants peuvent être adaptés selon le besoin.  
On veillera à créer un répertoire si et seulement si il propose plusieurs sous-répertoire ou fichiers.

1. Version des APIs (v1, v2, v3, etc.)
2. Nom de l'algorithme ou de la ressource utilisés ou de la langue
3. Nom de l'action ou de l'algorithme caractérisant le traitement
4. (...)

Exemples :

- `/v1/test/analyse`
- `/v2/istex/expand`
- `/v1/en/stemmer/analyse`

### Fichier d'exemple

Chaque répertoire dédié à une instance contiendra au moins un fichier `examples.http`.  
Ce fichier permet d'exécuter rapidement et simplement des exemples simples d'utilisation des webservices à travers VSCode
ou `restcli` (en ligne de commande).  
Le format du fichier est celui précisé dans la documentation vscode <https://github.com/Huachao/vscode-restclient#usage>.

Voir aussi [Tester](#tester).

## Créer une version

Afin de faciliter le déploiement (et de le limiter à un répertoire), on peut
utiliser les _helpers_ du Makefile (`make version-major`, `make version-minor`,
`make version-patch`).

Ils se chargent de mettre un _tag_ git sur répertoire et de le pousser sur le dépôt.
Leur seul paramètre est le nom du répertoire.

Exemple:

```bash
$ make version-major terms-extraction
Décompte des objets: 1, fait.
Écriture des objets: 100% (1/1), 185 bytes | 185.00 KiB/s, fait.
Total 1 (delta 0), reused 0 (delta 0)
remote: Updating references: 100% (1/1)
To ssh://gitbucket.inist.fr:22222/tdm/web-services.git
 * [new tag]         terms-extraction@1.0.0 -> terms-extraction@1.0.0
Nouvelle version créée: terms-extraction@1.0.0
URL à utiliser: https://gitbucket.inist.fr/tdm/web-services/archive/terms-extraction/terms-extraction@1.0.0.zip
```

L'URL fournie sert à la configuration de l'instance, et permet la mise à jour du
programme.