Skip to content
This repository was archived by the owner on Jul 21, 2023. It is now read-only.

Backend Gestore Personale

Alberto Schiabel edited this page May 16, 2019 · 1 revision

Moduli Gestore Personale

La logica operativa degli endpoint REST dello User Manager è contenuta nella sotto cartella src/modules. Essa contiene tante sottocartelle quanti sono i principali grouppi di risorse che il Gestore Personale permette di gestire:

  • users: contiene la logica operativa CRUD per la gestione degli utenti;
  • projects: contiene la logica operativa CRUD per la gestione dei progetti;
  • subscriptions: contiene la logica operativa CRUD per la gestione delle preferenze degli utenti riguardo a specifici eventi dei progetti disponibili;
  • userContacts: contiene la logica operativa CRUD per la gestione degli account di contatto degli utenti tramite le piattaforme TELEGRAM, SLACK e EMAIL;
  • search: contiene la logica per la ricerca degli utenti che possono essere interessati ad un certo tipo di evento.

Esiste inoltre il modulo health, il cui scopo è quello di permettere di capire il consumo di risorse del server e se il server sia pronto a ricevere richieste o meno.

Database

Il database utilizzato dal Gestore Personale è Postgres 11, e la definizione delle tabelle, procedure e quant'altro componga la logica lato database dello User Manager risiede nel file user-manager/user-manager-database/sql/ddl.sql.

Andiamo di seguito a presentare i tipi e le tabelle più importanti della definizione del database di questo servizio.

Enum

Per convenzione, ogni valore che compone un tipo enumerabile è salvato nel formato SCREAMING_CAMEL_CASE, ovvero in maiuscolo e con ogni parola separata da un simbolo di underscore (_). Inoltre, per tutti i tipi enumerabili presentati di seguito, è possibile utilizzare la stored function get_enum_values(regtype) per ottenere la lista completa e aggiornata degli elementi che definisco il tipo enumerabile passato come argomento.

public.producer_service

Questo tipo enumera la lista dei servizi di produzione supportati. Attualmente, i servizi di produzione supportati sono i seguenti:

  • REDMINE
  • GITLAB
  • SONARQUBE

Query per ottenere la lista aggiornata degli elementi di questo tipo:

SELECT * FROM get_enum_values('public.producer_service');

public.consumer_service

Questo tipo enumera la lista dei servizi di consumo supportati. Attualmente, i servizi di consumo supportati sono i seguenti:

  • TELEGRAM
  • EMAIL
  • SLACK

Query per ottenere la lista aggiornata degli elementi di questo tipo:

SELECT * FROM get_enum_values('public.consumer_service');

public.user_priority

Questo tipo enumera la lista di priorità supportate per un utente. Attualmente, le priorità utente supportate sono le seguenti:

  • LOW
  • MEDIUM
  • HIGH

Query per ottenere la lista aggiornata degli elementi di questo tipo:

SELECT * FROM get_enum_values('public.user_priority');

public.service_event_type

Questo tipo enumera la lista di tipologie di evento supportate per ogni servizio di produzione registrato nel sistema. Ogni tipologia di evento è salvata con il prefisso ${SERVICE}_, dove ${SERVICE} indica un servizio di produzione tra quelli supportati. Di seguito sono presentate le tipologie di evento supportate, raggruppate per servizio di produzione relativo.

Tipologie di evento per REDMINE:

  • REDMINE_TICKET_CREATED: corrisponde all'evento di creazione di una nuova segnalazione in Redmine;
  • REDMINE_TICKET_EDITED: corrisponde all'evento di modifica di una segnalazione in Redmine.

Tipologie di evento per GITLAB:

  • GITLAB_COMMIT_CREATED: a partire da un singolo evento di push del codice sorgente in una repository ospitata su GitLab, Butterfly emette tanti eventi di creazione commit quanti sono i commit coinvolti nel push corrente, con un limite massimo di 20 commit (footnote: https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#push-events);
  • GITLAB_ISSUE_CREATED: a partire da un evento relativo ad una issue di GitLab, se non è presente una data di modifica, Butterfly interpreta tale evento come uno di creazione issue;
  • GITLAB_ISSUE_EDITED: a partire da un evento relativo ad una issue di GitLab, se è presente una data di modifica, Butterfly interpreta tale evento come uno di modifica issue;
  • GITLAB_MERGE_REQUEST_CREATED: corrisponde all'evento di apertura di una nuova merge request in GitLab;
  • GITLAB_MERGE_REQUEST_EDITED: corrisponde all'evento di modifica del contenuto di una merge request in GitLab;
  • GITLAB_MERGE_REQUEST_MERGED: corrisponde all'evento di merge di una merge request in GitLab;
  • GITLAB_MERGE_REQUEST_CLOSED: corrisponde all'evento di chiusura senza merge di una merge request in GitLab.

Tipologie di evento per SONARQUBE:

  • SONARQUBE_PROJECT_ANALYSIS_COMPLETED: corrisponde all'evento di completamento dell'analisi statica di un progetto con SonarQube.

Tabelle

Nella seguente sezione, con la sigla PK identificheremo la chiave primaria, mentre con FK andremo ad indicare una chiave esterna.

public.service

La tabella public.service assegna un ID relativo ai servizi di produzione come TELEGRAM per poterli collegare in seguito con la relazione rappresentata dalla tabella public.x_service_event_type. In questa tabella, neessun elemento è nullable e non è presente alcun valore di default.

Nome Tipo Esempio Commento
service_id bigint 2 PK
producer_service_key public.service GITLAB Servizio di produzione, unico

public.event_type

La tabella public.event_type assegna un ID relativo a tipologie di evento come GITLAB_COMMIT_CREATED per poterli collegare in seguito con la relazione rappresentata dalla tabella public.x_service_event_type. In questa tabella, neessun elemento è nullable e non è presente alcun valore di default.

Nome Tipo Esempio Commento
event_type_id bigint 10 PK
event_type_key public.service_event_type GITLAB_COMMIT_CREATED Tipologia di evento di produzione, unico

public.x_service_event_type

La tabella public.x_service_event_type rappresenta l'associazione molti a molti tra i servizi di produzione di public.service e le tipologie di evento di public.event_type. Ad esempio, questa tabella permette di dire che l'evento GITLAB_COMMIT_CREATED è uno degli eventi supportati dal servizio di produzione GITLAB. In questa tabella, neessun elemento è nullable e non è presente alcun valore di default.

Nome Tipo Esempio Commento
x_service_event_type_id bigint 5 PK
service_id bigint 2 FK di public.service.service_id. ID di un servizio di produzione, unico insieme a event_type_key
event_type_id bigint 10 FK di public.event_type.event_type_id. ID di una tipologia di evento di produzione, unico insieme a service_id

public.project

La tabella public.project modella l'entità "progetto" del Gestore Utente. Ogni progetto è identificato da un nome e può avere una URL associata per ogni servizio di produzione supportato. Ad esempio, il progetto chiamato "Butterfly" può essere citato sia in eventi provenienti da GitLab (ad esempio da https://gitlab.com/dstack/butterfly) sia da eventi provenienti di analisi provenienti da SonarQube (ad esempio da https://sonarcloud.io/dstack/butterfly). Non può esistere progetto senza almeno una URL associata. Le URL dei progetti sono salvati in una serializzazione binaria del formato JSON, e sono costruite come una mappa chiave-valore in cui la chiave è il nome del sistema di produzione (ad esempio GITLAB) e il valore è la rispettiva URL (ad esempio https://gitlab.com/dstack/butterfly). È compito delle query e delle funzioni che interagiscono con la tabella public.project assicurarsi che tale struttura JSON sia rispettata. Il vincolo di univocità per il nome di un progetto è case insensitive, grazie all'indice project_name_unique_idx. Solo il campo modified è nullable, e solo il campo created ha un valore di default (corrispondente alla data e all'ora corrente). La cancellazione di un progetto comporta la distruzione di tutti i record della tabella public.subscription in cui è coinvolto il progetto eliminato.

Nome Tipo Esempio Commento
project_id bigint 1 PK
project_name varchar(30) 'Butterfly' Nome del progetto, unico
project_url jsonb '{"GITLAB":"https://gitlab.com/dstack/butterfly"}' Mappa delle URL associate al progetto
created timestamp '2019-05-02 14:48:25.7927+00' Data e ora di creazione
modified timestamp NULL Data e ora di ultima modifica

public.user

La tabella public.user modella l'entità "utente" del Gestore Utente. Ogni utente è identificato da una email e possiede un nome ed un cognome. Gli utenti possono essere abilitati o disabilitati grazie al flag booleano enabled, che assume il valore true come impostazione predefinita. Gli utenti disabilitati non compaiono nelle query di ricerca esposte dalle REST API dello User Manager. Il vincolo di univocità per l'email di un utente è case insensitive, grazie all'indice user_email_unique_idx. Solo il campo modified è nullable, e solo il campo created ha un valore di default (corrispondente alla data e all'ora corrente). La cancellazione di un utente comporta la distruzione di tutti i record delle tabelle public.subscription e public.user_contact in cui è coinvolto l'utente eliminato.

Nome Tipo Esempio Commento
user_id bigint 1 PK
email varchar(30) 'alberto.schiabel@gmail.com' Email dell'utente, unica
firstname varchar(30) 'Alberto' Nome dell'utente
lastname varchar(30) 'Schiabel' Cognome dell'utente
enabled boolean true Vero solo se l'utente corrente è abilitato
created timestamp '2019-05-02 14:48:25.7927+00' Data e ora di creazione
modified timestamp NULL Data e ora di ultima modifica

public.user_contact

La tabella public.user_contact modella l'entità "account di contatto" del Gestore Utente. Butterfly permette la registrazione di tanti account di contatto diversi quanti sono i sistemi di contatto enumerati in public.consumer_service, quindi i sistemi di contatto attualmente supportati sono TELEGRAM, EMAIL e SLACK. La definizione degli account di contatto permette al servizio middleware-dispatcher di inoltrare i messaggi degli eventi verso il Consumer corretto, e permette a tali Consumer di sapere a chi inviare tali messaggi.

Per ogni account di contatto è necessario un riferimento di contatto, ovvero un identificativo che contraddistingua l'utente corrente all'interno della piattaforma di contatto scelta. Nel caso l'utente voglia ricevere notifiche su Telegram, il riferimento del sistema di contatto è rappresentato dall'ID della conversazione tra il bot Telegram di Butterfly (TODO: inserire link) e l'utente corrente. Per ulteriori informazioni su come questo campo sia popolato, consultare la sezione [TODO: riferimento alla sezione che spiega come usare e cosa fa il bot di Telegram]. Nel caso l'utente voglia ricevere notifiche tramite email, il riferimento del sistema di contatto è rappresentato dall'email specificata dall'utente. (TODO: / DISCUSS: al momento della registrazione di un utente potremmo creare automaticamente un account email il cui campo contact_ref sia quello dell'email con cui è stato creato l'utente).

Non possono esistere account appartenenti ad utenti diversi in cui la piattaforma e il riferimento di contatto siano uguali. Ad esempio, due utenti non possono avere entrambi un contatto Telegram il cui ID della conversazione con il bot Telegram sia il medesimo. Gli utenti che non hanno ancora alcun sistema di contatto associato non compariranno mai tra i possibili candidati alla ricezione della notifica di un evento di un sistema di produzione. In questa tabella, nessun elemento è nullable e non è presente alcun valore di default.

Nome Tipo Esempio Commento
user_contact_id bigint 1 PK
user_id bigint 1 FK di public.user.user_id. ID dell'utente associato all'account di contatto
contact_type public.consumer_service TELEGRAM Piattaforma di contatto scelta dall'utente per ricevere notifiche di Butterfly
contact_ref varchar(30) '38442289' Riferimento di contatto per la piattaforma scelta dall'utente

Funzioni associate

Per semplificare la gestione e l'interazione con la tabella public.user_contact, il database di Butterfly offre alcune stored function dedicate, presentate di seguito.

Alcune delle funzioni relative agli account di contatto hanno come tipo di ritorno public.user_contact_denormalized_type, la cui definizione è la seguente:

CREATE TYPE public.user_contact_denormalized_type AS (
  "userContactId" BIGINT,
  "userEmail" text,
  "contactService" public.consumer_service,
  "contactRef" text
);

public.create_user_contact(text, public.consumer_service, text) è la funzione utilizzata per creare un nuovo account di contatto per un particolare utente, identificato dalla sua email. L'intestazione della funzione è:

FUNCTION public.create_user_contact(
  in_user_email text,                             -- Email dell'utente a cui sarà associato il nuovo account di contatto
  in_contact_type public.consumer_service,        -- Nome della piattaforma di contatto scelta: TELEGRAM, EMAIL, SLACK 
  in_contact_ref text                             -- Identificativo univoco della destinazione del destinatario associata alla piattaforma di contatto
)
RETURNS public.user_contact_denormalized_type

public.delete_user_contact(text, public.consumer_service) è la funzione utilizzata per la eliminare una particolare iscrizione. L'intestazione della funzione è:

FUNCTION public.delete_user_contact(
  in_user_email text,                             -- Email dell'utente a cui è associato l'account di contatto da eliminare
  in_contact_type public.consumer_service         -- Nome della piattaforma di cui andare ad eliminare l'account per l'utente corrente: TELEGRAM, EMAIL, SLACK 
)
RETURNS INT

public.keyword

La tabella public.keyword contiene la definizione di tutte le parole chiavi associate alle iscrizioni di ogni utente. Visto che le parole chiavi possono essere molteplici e spesso possono ripetersi (si pensi alle parole chiavi "BUG" e "FIX"), per ogni iscrizione sono salvati solo gli id delle parole chiavi coinvolte. La tabella che determina l'associazione vera e propria, di tipo molti a molti, è public.x_subscription_keyword. In questa tabella, nessun elemento è nullable e non è presente alcun valore di default.

Nome Tipo Esempio Commento
keyword_id bigint 1 PK
keyword_name varchar(15) 'BUG' Valore della keyword, unico

public.subscription

La tabella public.subscription modella l'entità "iscrizione ad un evento" del Gestore Utente. L'iscrizione ad un evento è uno dei concetti chiave di Butterfly, perché è ciò che permette all'utente di essere candidato a ricevere le notifiche di un evento che coinvolge la tipologia di evento e il progetto della subscription corrispondente. Con un'iscrizione, un certo utente esprime la volontà di ricevere notifiche riguardanti una singola tipologia di evento (ad esempio solo notifiche sulla creazione di nuove issue in GitLab, piuttosto che notifiche sull'aggiornamento di una segnalazione in Redmine) che avviene in un determinato progetto. Ogni utente può esprimere molteplici iscrizioni, a patto che ognuna di esse riguardi allo stesso tempo progetti e tipi di evento diversi. Poiché più utenti diversi possono decidere di iscriversi allo stesso identico modello di evento riguardante lo stesso progetto, esiste la possibilità di specificare la priorità dell'utente corrente. Nella ricerca degli utenti a cui inoltrare le notifiche di un particolare evento, Butterfly sceglie sempre gli utenti con la priorità più alta.

Esistono due relazioni molti a molti che non sono direttamente desumibili dalla tabella sottostante. La tabella public.x_subscription_user_contact è utilizzata per registrare i molteplici sistemi di contatto in cui l'utente corrente desidera ricevere notifiche, mentre la tabella public.x_subscription_keyword è impiegata per il salvataggio delle parole chiavi di interesse per la tipologia di evento e per il progetto coinvolto da ogni subscription.

In questa tabella, nessun elemento è nullable e non è presente alcun valore di default.

Nome Tipo Esempio Commento
subscription_id bigint 1 PK
user_id bigint 1 FK di public.user.user_id. ID dell'utente associato all'iscrizione. Unico insieme a project_id e x_service_event_type_id.
project_id bigint 1 FK di public.project.project_id. ID del progetto associato all'iscrizione. Unico insieme a user_id e x_service_event_type_id
x_service_event_type_id bigint 1 FK di public.x_service_event_type.x_service_event_type_id. ID del progetto associato all'iscrizione. Unico insieme a user_id e project_id
user_priority public.user_priority HIGH Priorità dell'utente associato a questa iscrizione. Se un altro utente è registrato alla stessa tipologia di evento e allo stesso progetto, allora solo l'utente disponibile con priorità più alta riceverà il messaggio

Funzioni associate

Per semplificare la gestione e l'interazione con la tabella public.subscription, che come visto richiede più associazioni molti a molti con diverse tabelle, il database di Butterfly offre delle stored function dedicate, presentate di seguito.

Alcune delle funzioni relative iscrizioni a tipologie di evento hanno come tipo di ritorno public.subscription_denormalized_type, la cui definizione è la seguente:

CREATE TYPE public.subscription_denormalized_type AS (
  "subscriptionId" BIGINT,
  "userEmail" text,
  "projectName" text,
  "eventType" public.service_event_type,
  "userPriority" public.user_priority,
  "contacts" jsonb,
  "keywordList" text[]
);

public.create_subscription(text, text, public.service_event_type, public.user_priority, public.consumer_service[], text[]) è la funzione utilizzata per creare una nuova iscrizione ad una tipologia di evento per un particolare progetto. L'intestazione della funzione è:

FUNCTION public.create_subscription(
  in_user_email text,                             -- Email dell'utente che sta creando una nuova iscrizione
  in_project_name text,                           -- Nome del progetto 
  in_event_type_key public.service_event_type,    -- Nome della tipologia di evento a cui l'utente si sta iscrivendo, come GITLAB_COMMIT_CREATED, GITLAB_ISSUE_CREATED, ...
  in_user_priority public.user_priority,          -- Priorità dell'utente identificato `in_user_email`: LOW, MEDIUM, HIGH
  in_contact_type_list public.consumer_service[], -- Lista dei sistemi di contatto in cui l'utente vuole essere notificato: TELEGRAM, EMAIL, SLACK
  in_keyword_name_list text[]                     -- Lista di parole chiave che, se appaiono in un nuovo evento della tipologia e del progetto specificato da questa iscrizione,
                                                  -- comportano l'invio di una notifica all'utente corrente usando i sistemi di contatto specificati
)
RETURNS public.subscription_denormalized_type     -- Vista denormalizzata dell'iscrizione appena creata.

public.find_subscription(text, text, public.service_event_type) è la funzione utilizzata per la ricerca di una particolare iscrizione. L'intestazione della funzione è:

FUNCTION public.find_subscription(
  in_user_email text,                             -- Email dell'utente
  in_project_name text,                           -- Nome del progetto 
  in_event_type_key public.service_event_type     -- Nome della tipologia di evento, come GITLAB_COMMIT_CREATED, GITLAB_ISSUE_CREATED,
)
RETURNS public.subscription_denormalized_type     -- Vista denormalizzata dell'iscrizione individuata.

public.find_subscriptions_by_email(text) è la funzione utilizzata per la ricerca di tutte le iscrizioni di un particolare utente. L'intestazione della funzione è:

FUNCTION public.find_subscriptions_by_email(
  in_user_email text                              -- Email dell'utente
)
RETURNS public.subscription_denormalized_type     -- Vista denormalizzata delle iscrizioni individuate.

public.update_subscription(text, text, public.service_event_type, public.user_priority, public.consumer_service[], text[]) è la funzione utilizzata per effettuare aggiornamenti parziali ad un'iscrizione ad una tipologia di evento per un particolare progetto. L'intestazione della funzione è:

FUNCTION public.update_subscription(
  in_user_email text,                             -- Email dell'utente che sta creando una nuova iscrizione.
  in_project_name text,                           -- Nome del progetto.
  in_event_type_key public.service_event_type,    -- Nome della tipologia di evento a cui l'utente si sta iscrivendo, come GITLAB_COMMIT_CREATED, GITLAB_ISSUE_CREATED, ...
  in_user_priority public.user_priority,          -- Priorità dell'utente identificato `in_user_email`: LOW, MEDIUM, HIGH.
                                                  -- Se è NULL, viene mantenuta la priorità utente precedente.
                                                  -- Se non è NULL, la priorità utente per questa iscrizione viene sovrascritta con il nuovo valore.
  in_contact_type_list public.consumer_service[], -- Lista dei sistemi di contatto in cui l'utente vuole essere notificato: TELEGRAM, EMAIL, SLACK.
                                                  -- Se è NULL, vengono mantenuti i sistemi di contatto precedenti.
                                                  -- Se non è NULL, i sistemi di contatto per questa iscrizioni vengono eliminati e sostituiti con i nuovi valori.
  in_keyword_name_list text[]                     -- Lista di parole chiave che, se appaiono in un nuovo evento della tipologia e del progetto specificato da questa iscrizione,
                                                  -- comportano l'invio di una notifica all'utente corrente usando i sistemi di contatto specificati.
                                                  -- Se è NULL, vengono mantenute le parole chiave precedenti.
                                                  -- Se non è NULL, la precedente associazione di parole chiavi per questa iscrizione viene eliminata e sostituita
                                                  -- con i nuovi valori.
)
RETURNS public.subscription_denormalized_type     -- Vista denormalizzata dell'iscrizione dopo le modifiche appena effettuate.

public.delete_subscription(text, text, public.service_event_type) è la funzione utilizzata per la eliminare una particolare iscrizione. L'intestazione della funzione è:

FUNCTION public.delete_subscription(
  in_user_email text,                             -- Email dell'utente.
  in_project_name text,                           -- Nome del progetto.
  in_event_type_key public.service_event_type     -- Nome della tipologia di evento, come GITLAB_COMMIT_CREATED, GITLAB_ISSUE_CREATED, ...
)
RETURNS INT                                       -- Numero delle entità eliminate, assume valore 1 se esisteva un'iscrizione corrispondente ai parametri in input,
                                                  -- 0 altrimenti.

public.x_subscription_user_contact

La tabella public.x_subscription_user_contact rappresenta l'associazione molti a molti tra l'entità "iscrizione ad un evento" rappresentata da public.subscription e gli account di contatto dell'utente iscritto, contenuti nella tabella public.user_contact. Questa tabella è utilizzata per registrare i molteplici sistemi di contatto in cui l'utente corrente desidera ricevere notifiche.

In questa tabella, nessun elemento è nullable e non è presente alcun valore di default.

Nome Tipo Esempio Commento
subscription_id bigint 1 PK insieme a user_contact_id
user_contact_id varchar(15) 1 PK insieme a subscription_id

public.x_subscription_keyword

La tabella public.x_subscription_user_contact rappresenta l'associazione molti a molti tra l'entità "iscrizione ad un evento" rappresentata da public.subscription e le parole chiave scelte dall'utente per una particolare iscrizione, contenute nella tabella public.keyword. Questa tabella è impiegata per il salvataggio delle parole chiavi di interesse per la tipologia di evento e per il progetto coinvolto da ogni subscription. Questa tabella è utilizzata per registrare i molteplici sistemi di contatto in cui l'utente corrente desidera ricevere notifiche.

In questa tabella, nessun elemento è nullable e non è presente alcun valore di default.

Nome Tipo Esempio Commento
subscription_id bigint 1 PK insieme a keyword_id
keyword_id bigint 1 PK insieme a subscription_id

public.event_sent_log

La tabella public.event_sent_log è utilizzata per il salvataggio (opzionale) degli eventi ricevuti dai servizi di produzione. Questa tabella fornisce un semplice esempio di come sia possibile espandere il sistema Butterfly storicizzando gli eventi ricevuti, in modo tale da poter permettere l'ottenimento di statistiche nel tempo. La scrittura in tale tabella è regolata dal parametro booleano in_save_event dalla stored function public.search_event_receivers(json, boolean), il quale è impostato di default a false.

In questa tabella, nessun elemento è nullable e non è presente alcun valore di default.

Nome Tipo Esempio Commento
event_sent_log_id bigint 1 PK
producer_service_key public.producer_service 1 PK insieme a subscription_id
event_record json {"projectName": "Butterfly", "eventId": "2", ..., "userEmail": "admin@mail.it"} Snapshot in formato JSON dell'evento inoltrato dai servizi di produzione.