-
Notifications
You must be signed in to change notification settings - Fork 0
Backend 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.
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.
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.
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');
Questo tipo enumera la lista dei servizi di consumo supportati. Attualmente, i servizi di consumo supportati sono i seguenti:
- TELEGRAM
- SLACK
Query per ottenere la lista aggiornata degli elementi di questo tipo:
SELECT * FROM get_enum_values('public.consumer_service');
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');
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.
Nella seguente sezione, con la sigla PK identificheremo la chiave primaria, mentre con FK andremo ad indicare una chiave esterna.
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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
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 |
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 |
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.
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
|
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
|
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. |