Utilizzo della REST API

Una guida dettagliata per sviluppatori sull'uso della REST API di 4HSE: panoramica dell'architettura, metodi di autenticazione, regole di autorizzazione e operazioni CRUD. Il documento include un esempio per semplificare l'integrazione e l'utilizzo dell'API.

Introduzione

In questa guida descriveremo l’architettura di 4HSE e i servizi web che offre agli sviluppatori. Successivamente, costruiremo un esempio di codice che include l’uso dell’API e copre uno scenario di sviluppo comune.

API Standard

La piattaforma 4HSE fornisce un ampio set di servizi web che consentono di gestire i dati dei propri progetti. Tutti i servizi seguono lo Standard HTTP REST API e offrono operazioni CRUD sulle risorse.

Una risorsa è un elenco di attributi (ad esempio, una persona è descritta da nome, cognome, ecc.). Ogni risorsa ha un identificatore univoco, l’attributo id, che ne garantisce l’unicità. Le risorse sono raggruppate in collezioni in base al loro tipo (tutte le risorse di tipo “persona” sono raggruppate nella collezione “person”).

Per ogni collezione, generalmente, sono disponibili cinque endpoint di base:

- GET <collection>/index
- GET <collection>/view/<my_resource_id>
- POST <collection>/create {payload}
- PUT <collection>/update/<my_resource_id> {payload}
- DELETE <collection>/delete/<my_resource_id>

Può capitare che una collezione non segua esattamente questo standard. Tuttavia, tutte le API disponibili sono ben documentate qui secondo lo standard Open API.

Autenticazione

Per accedere alle API di 4HSE è necessario autenticarsi. Esistono due modalità di autenticazione:

  • oauth2
  • auth_code

Supponiamo di voler recuperare l’elenco delle sedi ordinate per nome. In tal caso, bisognerebbe chiamare l’API:
service.4hse.com/office/index?sort=-name&limit=50&offset=51

Se si effettua questa chiamata senza autenticazione, si riceverà il codice di risposta Unauthorized (401). È necessario utilizzare uno dei due metodi di autenticazione sopra indicati affinché la chiamata funzioni correttamente.

Questo è solo un esempio. Una spiegazione più dettagliata delle API e delle modalità di chiamata è fornita nei capitoli successivi.

oauth2

Utilizziamo lo standard oauth2 per autenticare gli utenti.

L’URI dell’API è: service.4hse.com/oauth2/token

Per ottenere un token, è necessario conoscere i seguenti dati:

  • client_id – un identificatore univoco che rappresenta il client
  • client_secret – la password associata al client_id
  • username – il tuo nome utente
  • password – la tua password

oppure, se hai già ottenuto un token:

  • client_id – un identificatore univoco che rappresenta il client
  • client_secret – la password associata al client_id
  • refresh_token – il token di aggiornamento che hai ricevuto in una chiamata precedente alla stessa API

Ogni cliente che desidera utilizzare l’API deve avere il proprio client_id e client_secret. Dovresti chiedere al supporto 4hse per ottenere questi due dati.

Quando accedi al servizio per la prima volta, devi utilizzare il tuo nome utente e la tua password (ovviamente anche client_id e client_secret):

REQUEST
---
URI: service.4hse.com/oauth2/token
Host: service.4hse.com
Method: POST
Request Payload:
{
 "client_id": "example_client_id",
 "client_secret": "example_client_secret",
 "grant_type": "password",
 "username": "example_user",
 "password": "example_password"
}

Successivamente, riceverai una risposta simile alla seguente:

RESPONSE
---
Status Code: 200 OK
Content-Type: application/json
Body:
{
 "access_token": "433b8caa8ea9839b5a732a9455ee50bef31fe4e0",
 "expires_in": 86400,
 "token_type": "Bearer",
 "scope": null,
 "refresh_token": "84c893bdd88fb52400c5d8fc246e81bd544a3486",
 "user_id": "example_user"
}

L’ access_token è il token bearer che puoi utilizzare per accedere a tutte le API. Come indicato, scade dopo 86400 secondi (24 ore). Come puoi vedere, nella risposta c’è anche il refresh_token, che puoi utilizzare per ottenere un nuovo access_token senza dover usare username e password. Ricorda che il refresh_token scade dopo 14 giorni. La chiamata in questo caso sarà simile a questa:

REQUEST
---
URI: service.4hse.com/oauth2/token
Host: service.4hse.com
Method: POST
Request Payload:
{
 "client_id": "example_client_id",
 "client_secret": "example_client_secret",
 "grant_type": "refresh_token",
 "refresh_token": "84c893bdd88fb52400c5d8fc246e81bd544a3486"
}

Questa chiamata ti restituirà un nuovo access_token e un nuovo refresh_token (quello precedente verrà disabilitato).

RESPONSE
---
Status Code: 200 OK
Content-Type: application/json
Body:
{
 "access_token": "b483aff34847b87786bb1a6c313b9e40b2bcf749",
 "expires_in": 86400,
 "token_type": "Bearer",
 "scope": null,
 "refresh_token": "c645d84be64791ccf1ee1f148c4f2281a97ae49b",
 "user_id": "example_user"
}

Per effettuare una chiamata al sistema utilizzando il bearer token, devi aggiungere a tutte le tue richieste un nuovo header, come nell’esempio sopra:

Authorization: Bearer b483aff34847b87786bb1a6c313b9e40b2bcf749

Ora possiamo tornare alla precedente API che hai chiamato: service.4hse.com/office/index?sort=-name&limit=50&offset=51. Come ricordi, senza autenticazione hai ricevuto il codice di risposta Unauthorized (401). Ma ora che hai il bearer token, puoi effettuare la stessa chiamata aggiungendo l’header di autorizzazione richiesto:

REQUEST
---
URI: service.4hse.com/office/index?sort=-name&limit=50&offset=51
Host: service.4hse.com
Method: GET
Content-Type: application/json
Authorization: Bearer b483aff34847b87786bb1a6c313b9e40b2bcf749

Effettuando la chiamata in questo modo, riceverai il codice Success (200) e l’elenco degli uffici richiesti.

access-token

Questo metodo è molto semplice, poiché è necessario aggiungere solo il parametro di query access-token a ogni chiamata API. Devi contattare il supporto 4hse per ottenere l’access-token. Questo sarà generato su tua richiesta.

Tieni presente che questo access-token è completamente diverso e non ha alcuna relazione con l’oauth2.

Quando hai questo token, puoi effettuare la stessa chiamata precedente aggiungendo il parametro di query access-token. Se il tuo access token è sample-access-token, puoi semplicemente chiamare:

service.4hse.com/office/index?sort=-name&limit=50&offset=51&access-token=sample-access-token

Effettuando la chiamata in questo modo, riceverai il codice Success (200) e l’elenco degli uffici richiesti.

Autorizzazione (ACL)

Tutti i servizi web operano all’interno di un sistema di controllo degli accessi. Le regole di controllo degli accessi seguono le autorizzazioni definite per l’utente corrente.

Esempio

L’utente user-1 è manager di office-1 e non ha alcun accesso a office-2. Se user-1 tenta di accedere a office-2 chiamando il servizio office/view/office-2, il server risponde con il codice di stato 404 Not found perché l’utente non è autorizzato a visualizzare la risorsa office-2.

Questo comportamento è comune a tutti i servizi, tranne per index. Quando un utente chiede l’elenco delle risorse (una collezione) utilizzando index, il server risponde con l’elenco delle risorse che l’utente può vedere in base alle sue regole di controllo accessi.

Esempio

L’utente user-1 è manager di office-1 e office-3, e chiama office/index. Il server risponde con un elenco di uffici che contiene solo office-1 e office-3. L’office-2 viene saltato perché l’utente non può accedervi.

Chiamate

INDEX

Questa chiamata restituisce un elenco delle risorse della collezione a cui si riferisce. Parametri opzionali:

  • sort(1): ordina il risultato in base a un attributo della risorsa. Per impostazione predefinita l’ordinamento è in modalità ASC. Per ordinare in modalità DESC aggiungi il prefisso -.
  • limit(2): limita il set di risultati a un numero massimo di elementi. Per impostazione predefinita il valore è 100.
  • offset(3): l’indice del primo elemento del set di risultati.

Per parametri specifici della collezione (filtri, dati aggiuntivi ecc.) puoi consultare la nostra Documentazione API.

REQUEST
---
URI: service.4hse.com/office/index?sort=-name&limit=50&offset=51
Host: service.4hse.com
Method: GET
Content-Type: application/json
Query String Parameters:
 sort: -name // <b>(1)</b>
 limit: 50 // <b>(2)</b>
 offset: 51 // <b>(3)</b>

La risposta contiene:

  • data(4): elenco delle risorse che soddisfano i parametri della richiesta
  • total_count(5): il numero di elementi che corrispondono alla query corrente. Potrebbe essere diverso dal numero delle risorse presenti in data.
  • pos(6): l’offset corrente.
RESPONSE
---
Status Code: 200 OK
Content-Type: application/json
Body:
{
 "data": [
 {
 "id": "office-2-534443", // <b>(4)</b>
 "office_id": "office-2-534443",
 "name": "Office 2",
 ...
 },
 {
 "id": "office-1-213414",
 "office_id": "office-1-213414",
 "name": "Office 1",
 ...
 },
 ...
 ],
 "total_count": "2000" // <b>(5)</b>
 "pos": 51 // <b>(6)</b>
}

VIEW

Restituisce una risorsa con i suoi dettagli. Il parametro obbligatorio è id(1), necessario per identificare la risorsa. Questo parametro sarà riportato due volte nella risposta come attributi id(2) e <collection>_id(3). Una sede avrà gli attributi id e office_id con lo stesso valore.

Per parametri specifici della collezione (filtri, dati aggiuntivi ecc.) puoi consultare la nostra Documentazione API.

REQUEST
---
URI: service.4hse.com/office/view/office-1-534443
Host: service.4hse.com
Method: GET
Query String Parameters:
 id: office-1-534443 // <b>(1)</b>

La risposta contiene la risorsa nel body.

RESPONSE
---
Status Code: 200 OK
Content-Type: application/json
Body:
{
 "data": {
 "id": "office-1-534443", // <b>(2)</b>
 "office_id": "office-1-534443", // <b>(3)</b>
 "name": "Office 1",
 ...
 }
}

CREATE

Richiede un payload(1) contenente l’attributo <collection>_id(2) e tutti gli attributi richiesti per la risorsa.

Per parametri specifici della collezione puoi consultare la nostra Documentazione API.

REQUEST
---
URI: service.4hse.com/office/create
Host: service.4hse.com
Method: POST
Request Payload: // <b>(1)</b>
{
 "office_id": "office-1-534443", // <b>(2)</b>
 "name": "Office 1",
 ...
}

La risposta contiene la risorsa creata all’interno del body.

RESPONSE
---
Status Code: 201 Created
Content-Type: application/json
Body:
{
 "data": {
 "id": "office-1-534443",
 "office_id": "office-1-534443",
 "name": "Office 1",
 ...
 }
}

UPDATE

Come per il view, è necessario il parametro id(1) per identificare la risorsa. Inoltre, è necessario fornire un payload(2) contenente id(3), <collection>_id(4) e gli attributi aggiornati (5).

REQUEST
---
URI: service.4hse.com/office/update/office-1-534443
Host: service.4hse.com
Method: PUT
Query String Parameters:
 id: office-1-534443 //<b>(1)</b>
Payload: //<b>(2)</b>
 {
 "id": "office-1-534443", //<b>(3)</b>
 "office_id": "office-1-534443", //<b>(4)</b>
 "name":"new name for office 1", //<b>(5)</b>
 ...
 }

La risposta contiene la risorsa aggiornata all’interno del body.

RESPONSE
---
Status Code: 200 OK
Content-Type: application/json
Body:
{
 "data": {
 "id": "office-1-534443",
 "office_id": "office-1-534443",
 "name":"new name for office 1",
 ...
 }
}

DELETE

È necessario il parametro id(1) per identificare la risorsa da eliminare.
Inoltre, è possibile specificare il parametro force(2), che per impostazione predefinita è false.
Quando force = false, il servizio non elimina la risorsa, ma risponde con una richiesta di conferma che include eventualmente le risorse correlate a quella selezionata.

Ogni elemento in 4HSE fa parte di una struttura gerarchica di dipendenze. Se si elimina una persona, tutte le risorse ad essa collegate (certificati, formazioni, ecc.) verranno eliminate insieme ad essa. Questa pratica garantisce la coerenza dei dati ed evita elementi orfani.

REQUEST
---
URI: service.4hse.com/office/delete/office-1-534443
Host: service.4hse.com
Method: DELETE
Query String Parameters:
 id: office-1-534443 // <b>(1)</b>
 force: false // <b>(2)</b>

Quando force = false, il servizio risponde con 400 Bad Request e include nel body le risorse correlate.
Ogni risorsa collegata è identificata da:

  • id(3): identificativo univoco della risorsa
  • label(4): etichetta descrittiva della risorsa
  • type(5): tipologia della risorsa
RESPONSE
---
Status Code: 400 Bad Request
Content-Type: application/json
Body:
{
 "message": "Addictions are present",
 "code": 1001,
 "data": [
 {
 "id": "office-1-534443", // <b>(3)</b>
 "label": "Office 1", // <b>(4)</b>
 "type": "OFFICE" // <b>(5)</b>
 }
 ]
}

Se force = true(2), il servizio elimina la risorsa identificata da id(1) e tutte le risorse correlate.

REQUEST
---
URI: service.4hse.com/office/delete/office-1-534443
Host: service.4hse.com
Method: DELETE
Query String Parameters:
 id: office-1-534443 //<b>(1)</b>
 force: true //<b>(2)</b>
RESPONSE
---
Status Code: 204 No Content
Content-Type: application/json

Esempio di codice

In questo esempio di codice realizzeremo una semplice applicazione CRUD che utilizza le API REST di 4HSE.

Requisiti:

  • Ambiente di lavoro con Python 3
  • Libreria python-requests
  • Libreria python-uuid

Iniziamo scrivendo il codice per recuperare una lista di uffici chiamando il servizio office/index.

import requests <b>(1)</b>

params = {'limit': 10, 'sort': '-name'} <b>(2)</b>
r = requests.get('https://service.4hse.com/office/index', params=params) <b>(3)</b>
print(r.text) #{data: [{'office_id':'sad14024000hdg002252','name':'My first office', 'project_id':'prj1234567'}], total_count: 1, pos: 0}
print(r) #<Response [200]>
  1. Importa la libreria per effettuare richieste HTTP.
  2. Definisci un insieme di parametri per la richiesta. In questo caso, vogliamo limitare il set di risultati a 10 risorse, ordinate in ordine decrescente per nome della sede.
  3. Effettua la richiesta e stampa i risultati.

Il prossimo passo è la creazione di una nuova sede utilizzando il servizio office/create.

import uuid

project_id = r.json().get('data')[0].get('project_id') # es. prj1234567 <b>(1)</b>
office_id = uuid.uuid1() # es. 00a5d37c-4fe2-46ee-b820-77ce0ac22725 <b>(2)</b>

payload = {'office_id': office_id, 'name': 'My new office', 'project_id': project_id} <b>(3)</b>
r = requests.post('https://service.4hse.com/office/create', data=payload, params=authentication) <b>(4)</b>
print(r.text) #{'data': {'office_id': '00a5d37c-4fe2-46ee-b820-77ce0ac22725', 'name': 'My new office', 'project_id': 'prj1234567'} }
print(r) #<Response [201]>
  1. Un attributo obbligatorio per la sede è project_id, che identifica il progetto all’interno del quale vogliamo creare la sede. In questo esempio, lo recuperiamo dalla prima sede restituito dalla chiamata index.
  2. Utilizza la libreria uuid di Python per generare un identificativo univoco per la sede.
  3. Definisci un payload che includa office_id, project_id e l’attributo obbligatorio name.
  4. Effettua la richiesta e stampa i risultati.

Ora andremo a recuperare la sede appena creata utilizzando il servizio office/view.

params = {'id': office_id} <b>(1)</b>
r = requests.get('https://service.4hse.com/office/view', params=params) <b>(2)</b>
print(r.text) #{'data': {'office_id': '00a5d37c-4fe2-46ee-b820-77ce0ac22725', 'name': 'My new office', 'project_id': 'prj1234567'} }

print(r) #<Response [200]>
new_office = r.json().get('data') <b>(3)</b>
  1. Definisci i parametri includendo office_id.
  2. Effettua la richiesta e stampa il risultato.
  3. Infine, memorizziamo la sede appena creata all’interno di new_office.

Ora vogliamo modificare il nome della nostra sede utilizzando il servizio office/update.

params = {'id': office_id}
payload = new_office
payload.update({'name': 'new name for my office'}) <b>(1)</b>
r = requests.put('https://service.4hse.com/office/update', data=payload, params=params) <b>(2)</b>
print(r.text) #{'data': {'office_id': '00a5d37c-4fe2-46ee-b820-77ce0ac22725', 'name': 'new name for my office', 'project_id': 'prj1234567'} }
print(r) #<Response [200]>
  1. Usa new_office come payload della richiesta e cambia il suo attributo name.
  2. Effettua la richiesta e stampa il risultato.

Infine, eliminiamo la sede utilizzando il servizio office/delete.

params = {'id': office_id, 'force': 'true'} <b>(1)</b>
r = requests.delete('https://service.4hse.com/office/delete', params=params) <b>(2)</b>
print(r) #<Response [204]>
  1. Definisci i parametri includendo l’office_id che vogliamo eliminare e force = true per confermare che vogliamo eliminare l’elemento.
  2. Effettua la richiesta e stampa il risultato.

Fonti: https://github.com/4hse/example-rest-api-client