MACOEV.Glpi.Client 26.14.0

MACOEV.Glpi.Client

Client .NET per le API REST di GLPI 10+, condiviso fra il portale di ticketing ASL Roma 1 / PTV e il modulo Inventario Semplificato.

Target: net8.0 e net10.0.

Perche' esiste

L'integrazione GLPI viveva dentro l'applicazione Blazor di ticketing, in un unico file da 1188 righe non riusabile da fuori. Questa libreria la estrae e ne sana i difetti strutturali:

Prima Adesso
new HttpClient() a ogni chiamata (~45 volte, alcune non disposte) un solo client dalla factory, con pipeline di resilienza
Sessione Scoped: una per circuito Blazor, mai chiusa IGlpiSessionProvider singleton, thread-safe, con scadenza e killSession
Nessuna reazione al 401: la sessione scaduta faceva fallire tutto rinnovo automatico e un solo ritentativo
Console.WriteLine ILogger
Termini di ricerca interpolati in query string GlpiHttpClient.Escape
Errori GLPI opachi GlpiApiException con ErrorCode tipizzato

Uso

builder.Services.AddGlpiClient(builder.Configuration);
{
  "Glpi": {
    "BaseUri": "https://glpi.aslroma1.it",
    "AppToken": "...",
    "UserToken": "...",
    "SessionLifetime": "00:20:00"
  }
}

Oppure in codice:

builder.Services.AddGlpiClient(options =>
{
    options.BaseUri = "https://glpi.aslroma1.it";
    options.AppToken = appToken;
    options.UserToken = userToken;
});

BaseUri e' la radice dell'istanza GLPI senza /apirest.php.

Nota sulla sessione

Le credenziali sono quelle di un service account: la sessione aperta dal client non e' quella dell'utente finale. Di conseguenza glpiactiveprofile restituito da initSession descrive il service account e non e' utilizzabile per autorizzare l'utente applicativo. Per i ruoli vanno interrogati Group_User / Profile_User sull'utente autenticato.

Modelli

I DTO vivono tutti nel namespace unico Macoev.Glpi.Client.Models, organizzati in cartelle (Common, Session, Users, Tickets, Dropdowns, Assets, Documents, Knowbase): un solo using per il consumatore.

I nomi JSON sono il contratto con GLPI e sono coperti da test di contratto (ModelContractTests): un refuso in un JsonPropertyName non produce errori di compilazione, quindi la verifica non puo' che essere automatica.

Cosa resta fuori, di proposito

Non migrato Perche'
UserModel.Initials / FullName, DevicesSearchModel.ImageUrl, TicketInputModel.UserInfoContent Presentazione: dipendono da tema, logo e lingua dell'applicazione ospite
TicketInputModel (form completo), TicketInputMoveDetailModel Form model con stato UI (recapiti, consensi a tre stati, edificio/piano). La libreria espone il solo TicketInput, che porta i campi che finiscono davvero sul filo
Glpiactiveprofile, Glpiprofiles, GLPI_SessionModel, AuthenticationModel ~600 righe senza alcun uso: la proprieta' session che le conteneva e' commentata. Descrivono comunque il service account, non l'utente applicativo

Sviluppo

dotnet test

Servizi

Servizio Copre
IGlpiUserService lettura, provisioning e plugin field degli utenti
IGlpiTicketService creazione, ricerca, follow-up e validazioni
IGlpiDropdownService sedi, entita', categorie ITIL, dropdown dei plugin field
IGlpiCatalogService ricerche a catalogo su ConsumableItem
IGlpiDocumentService allegati dei ticket
IGlpiKnowbaseService articoli della Knowledge Base

Coprono l'intera superficie del precedente IGLPIHelper (27 metodi). Alcune scelte sono volutamente conservative, perche' il portale di ticketing in produzione ci si appoggia:

  • Tutte e tre le ricerche a catalogo interrogano ConsumableItem, anche dove il nome del metodo suggerirebbe altri itemtype. E' il comportamento in produzione.
  • CreateTicketAsync non solleva se GLPI rifiuta: restituisce Id = -1 con il motivo in Message, come si aspetta l'interfaccia utente.
  • GetUserAsync filtra per nome esatto dopo la chiamata: searchText di GLPI e' una LIKE, quindi "mrossi" restituisce anche "mrossini".
  • SearchUserByUsernameAsync e' mantenuto ma non funziona: su GLPI il criterio field=1 con equals restituisce sempre 0 risultati. Vedi il commento sull'interfaccia.

Modulo inventario

IGlpiInventoryService copre censimento singolo e massivo, ricerca per seriale o numero di inventario, cambio di stato e associazione Item_Ticket. IGlpiPluginFieldsService legge e scrive i campi dei container del plugin Fields.

Gli id delle searchoption non sono mai costanti in questi due servizi: vengono risolti a runtime dal catalogo, perche' il plugin Fields li rinumera a ogni campo aggiunto a un container e serial non ha lo stesso id su tutti gli itemtype.

Verificare la configurazione GLPI

export GLPI_URL=https://glpi-test.aslroma1.it
export GLPI_APP_TOKEN=...
export GLPI_USER_TOKEN=...
python3 tools/verifica-glpi.py

Controlla diritti sugli itemtype, anagrafiche popolate, presenza dei campi Fields attesi e dei gruppi dei ruoli. Con --write esegue anche creazione asset, scrittura dei campi e Item_Ticket. Non cancella nulla: gli elementi di prova creati vengono elencati a fine esecuzione. Esce con codice diverso da zero se qualche controllo fallisce, quindi e' utilizzabile come gate prima dell'avvio dello sviluppo.

Rilascio

La pubblicazione e' automatica su GitHub Actions ed e' legata al tag, non al branch:

git tag v26.8.1 && git push origin v26.8.1

Il workflow publish.yml ricava la versione dal tag (v26.8.1 -> 26.8.1), la inietta con -p:Version=, esegue i test, impacchetta e pubblica sul feed. Richiede il secret di repository NUGET_FEED_API_KEY. In alternativa si puo' lanciare a mano da Actions > Publish NuGet > Run workflow indicando la versione.

Versionamento YY.M.patch.build, coerente con gli altri pacchetti MACOEV.

Attenzione alla normalizzazione NuGet: una quarta cifra a zero viene rimossa, quindi 26.8.1.0 viene pubblicato come 26.8.1. Conviene taggare direttamente nella forma normalizzata, per non avere tag e pacchetto con nomi diversi.

Directory.Build.targets allinea AssemblyVersion e FileVersion alla Version finale, qualunque sia la sua provenienza.

No packages depend on MACOEV.Glpi.Client.

Version Downloads Last updated
26.14.0 9 10/20/2026
26.13.0 6 10/08/2026
26.12.1 5 10/07/2026
26.12.0 6 09/29/2026
26.11.1 3 09/28/2026
26.11.0 14 09/09/2026
26.10.1 1 09/08/2026
26.9.2 1 09/08/2026
26.9.1 2 09/08/2026
26.9.0 0 09/08/2026
26.8.9 1 09/03/2026
26.8.8 2 09/02/2026
26.8.7 0 09/02/2026
26.8.6 2 08/27/2026
26.8.5 2 08/27/2026
26.8.4 1 08/26/2026
26.8.3 1 08/26/2026
26.8.2 1 08/26/2026
26.8.1.5 0 08/26/2026