I metadati ti permettono di controllare e personalizzare la generazione del codice attraverso
semplici tag. Usa la funzione Meta
per aggiungere metadati ai tuoi elementi di design.
Per impostazione predefinita, Goa genera solo i tipi che sono utilizzati dai metodi del servizio. Se definisci un tipo nel tuo design ma non lo referenzi in alcun parametro o risultato di metodo, Goa salterà la sua generazione.
Il tag di metadati "type:generate:force"
sovrascrive questo comportamento. Accetta
nomi di servizi come argomenti per specificare quali servizi dovrebbero includere il tipo nel
loro codice generato. Se non vengono forniti nomi di servizi, il tipo verrà
generato per tutti i servizi:
var MyType = Type("MyType", func() {
// Forza la generazione del tipo in service1 e service2, anche se non utilizzato
Meta("type:generate:force", "service1", "service2")
Attribute("name", String)
})
var OtherType = Type("OtherType", func() {
// Forza la generazione del tipo in tutti i servizi
Meta("type:generate:force")
Attribute("id", String)
})
Puoi controllare dove vengono generati i tipi utilizzando i metadati del pacchetto. Per impostazione predefinita, i tipi vengono generati nei rispettivi pacchetti di servizio, ma puoi generarli in un pacchetto condiviso. Questo è particolarmente utile quando più servizi devono lavorare con le stesse struct Go, come quando si condivide logica di business o codice di accesso ai dati. Generando i tipi in un pacchetto condiviso, eviti di dover convertire tra definizioni di tipo duplicate tra i servizi:
var CommonType = Type("CommonType", func() {
// Genera nel pacchetto types condiviso
Meta("struct:pkg:path", "types")
Attribute("id", String)
Attribute("createdAt", String)
})
Questo crea una struttura come:
project/
├── gen/
│ └── types/ # Pacchetto types condiviso
│ └── common_type.go # Generato da CommonType
types
è comunemente utilizzato per i tipi condivisiPer impostazione predefinita, Goa genera nomi di campo convertendo i nomi degli attributi in CamelCase. Per esempio, un attributo chiamato “user_id” diventerebbe “UserID” nella struct generata.
Goa fornisce anche mappature predefinite dai tipi di design ai tipi Go:
String
→ string
Int
→ int
Int32
→ int32
Int64
→ int64
Float32
→ float32
Float64
→ float64
Boolean
→ bool
Bytes
→ []byte
Any
→ any
Puoi personalizzare i singoli campi utilizzando diversi tag di metadati:
struct:field:name
: Sovrascrive il nome del campo generatostruct:field:type
: Sovrascrive il tipo del campo generatostruct:tag:*
: Aggiunge tag personalizzati alla structEcco un esempio che combina questi:
var Message = Type("Message", func() {
Meta("struct:pkg:path", "types")
Attribute("id", String, func() {
// Sovrascrive il nome del campo
Meta("struct:field:name", "ID")
// Aggiunge un tag MessagePack personalizzato
Meta("struct:tag:msgpack", "id,omitempty")
// Sovrascrive il tipo con un tipo personalizzato
Meta("struct:field:type", "bison.ObjectId", "github.com/globalsign/mgo/bson", "bison")
})
})
Questo genera la seguente struct Go:
type Message struct {
ID bison.ObjectId `msgpack:"id,omitempty"`
}
Quando si usa struct:field:type
:
Quando si lavora con i Protocol Buffer, puoi personalizzare il codice protobuf generato utilizzando diversi metadati:
Il metadato struct:name:proto
ti permette di sovrascrivere il nome del messaggio
protobuf generato. Per impostazione predefinita, Goa usa il nome del tipo dal tuo design:
var MyType = Type("MyType", func() {
// Cambia il nome del messaggio protobuf in "CustomProtoType"
Meta("struct:name:proto", "CustomProtoType")
Field(1, "name", String)
})
Il metadato struct:field:proto
ti permette di sovrascrivere il tipo di campo
protobuf generato. Questo è particolarmente utile quando si lavora con tipi protobuf
ben noti o tipi da altri file proto. Accetta fino a quattro argomenti:
var MyType = Type("MyType", func() {
// Sovrascrittura semplice del tipo
Field(1, "status", Int32, func() {
// Cambia da sint32 predefinito a int32
Meta("struct:field:proto", "int32")
})
// Uso del tipo timestamp ben noto di Google
Field(2, "created_at", Timestamp, func() {
Meta("struct:field:proto",
"google.protobuf.Timestamp", // Tipo Proto
"google/protobuf/timestamp.proto", // Importazione Proto
"Timestamp", // Tipo Go
"google.golang.org/protobuf/types/known/timestamppb") // Importazione Go
})
})
Questo genera la seguente definizione protobuf:
import "google/protobuf/timestamp.proto";
message MyType {
int32 status = 1;
google.protobuf.Timestamp created_at = 2;
}
Il metadato protoc:include
specifica i percorsi di importazione utilizzati quando si invoca il
compilatore protoc. Puoi impostarlo sia a livello di API che di servizio:
var _ = API("calc", func() {
// Percorsi di importazione globali per tutti i servizi
Meta("protoc:include",
"/usr/include",
"/usr/local/include")
})
var _ = Service("calculator", func() {
// Percorsi di importazione specifici del servizio
Meta("protoc:include",
"/usr/local/include/google/protobuf")
// ... metodi del servizio ...
})
Quando impostato su una definizione API, i percorsi di importazione si applicano a tutti i servizi. Quando impostato su un servizio, i percorsi si applicano solo a quel specifico servizio.
struct:field:proto
deve fornire tutte le informazioni di importazione necessarie quando si utilizzano tipi proto esterniprotoc:include
dovrebbero puntare a directory contenenti file .protoControlla la generazione e la formattazione delle specifiche OpenAPI:
var _ = API("MyAPI", func() {
// Controlla la generazione OpenAPI
Meta("openapi:generate", "false")
// Formatta l'output JSON
Meta("openapi:json:prefix", " ")
Meta("openapi:json:indent", " ")
})
Questo influenza come viene formattato il JSON OpenAPI:
{
"openapi": "3.0.3",
"info": {
"title": "MyAPI",
"version": "1.0"
}
}
Puoi personalizzare come le operazioni e i tipi appaiono nella specifica OpenAPI utilizzando diversi metadati:
Il metadato openapi:operationId
ti permette di personalizzare come vengono
generati gli ID delle operazioni. Supporta segnaposto speciali che vengono sostituiti con valori
effettivi:
{service}
- sostituito con il nome del servizio{method}
- sostituito con il nome del metodo(#{routeIndex})
- sostituito con l’indice della rotta (solo quando un metodo ha più rotte)Per esempio:
var _ = Service("UserService", func() {
Method("ListUsers", func() {
// Genera operationId: "users/list"
Meta("openapi:operationId", "users/list") // Valore statico
})
Method("CreateUser", func() {
// Genera operationId: "UserService.CreateUser"
Meta("openapi:operationId", "{service}.{method}")
})
Method("UpdateUser", func() {
// Per rotte multiple, genera:
// - "UserService_UpdateUser_1" (prima rotta)
// - "UserService_UpdateUser_2" (seconda rotta)
Meta("openapi:operationId", "{service}_{method}_{#routeIndex}")
HTTP(func() {
PUT("/users/{id}") // Prima rotta
PATCH("/users/{id}") // Seconda rotta
})
})
})
Per impostazione predefinita, se non viene fornito alcun sommario tramite metadati, Goa genera un sommario:
Il metadato openapi:summary
ti permette di sovrascrivere questo comportamento predefinito. Il
sommario appare in cima a ogni operazione e dovrebbe fornire una breve
descrizione di ciò che fa l’operazione.
Puoi utilizzare:
{path}
che viene sostituito con il percorso HTTP dell’operazionevar _ = Service("UserService", func() {
Method("CreateUser", func() {
// Usa questa descrizione come sommario predefinito
Description("Crea un nuovo utente nel sistema")
HTTP(func() {
POST("/users")
})
})
Method("UpdateUser", func() {
// Sovrascrive il sommario predefinito
Meta("openapi:summary", "Gestisce la richiesta PUT a {path}")
HTTP(func() {
PUT("/users/{id}")
})
})
Method("ListUsers", func() {
// Nessun metadato di descrizione o sommario
// Il sommario predefinito sarà: "GET /users"
HTTP(func() {
GET("/users")
})
})
})
Questo genera la seguente specifica OpenAPI:
{
"paths": {
"/users": {
"post": {
"summary": "Crea un nuovo utente nel sistema",
"operationId": "UserService.CreateUser",
// ... altri dettagli dell'operazione ...
},
"get": {
"summary": "GET /users",
"operationId": "UserService.ListUsers",
// ... altri dettagli dell'operazione ...
}
},
"/users/{id}": {
"put": {
"summary": "Gestisce la richiesta PUT a /users/{id}",
"operationId": "UserService.UpdateUser",
// ... altri dettagli dell'operazione ...
}
}
}
}
Il metadato openapi:typename
ti permette di sovrascrivere come un tipo appare nella
specifica OpenAPI, senza influenzare il nome del tipo Go:
var User = Type("User", func() {
// Nella specifica OpenAPI, questo tipo sarà chiamato "CustomUser"
Meta("openapi:typename", "CustomUser")
Attribute("id", Int)
Attribute("name", String)
})
Goa ti permette di specificare esempi per i tuoi tipi nel design. Se non vengono
specificati esempi, Goa genera esempi casuali per impostazione predefinita. Il metadato openapi:example
ti permette di disabilitare questo comportamento di generazione degli esempi:
var User = Type("User", func() {
// Specifica un esempio (questo verrà utilizzato nella specifica OpenAPI)
Example(User{
ID: 123,
Name: "John Doe",
})
Attribute("id", Int)
Attribute("name", String)
})
var Account = Type("Account", func() {
// Disabilita la generazione di esempi per questo tipo
// Nessun esempio apparirà nella specifica OpenAPI
Meta("openapi:example", "false")
Attribute("id", Int)
Attribute("balance", Float64)
})
var _ = API("MyAPI", func() {
// Disabilita la generazione di esempi per tutti i tipi
Meta("openapi:example", "false")
})
Example()
per specificare esempi personalizzatiMeta("openapi:example", "false")
per impedire qualsiasi generazione di esempiopenapi:example
a false a livello di API influenza tutti i tipiPuoi aggiungere tag ed estensioni personalizzate alla tua specifica OpenAPI sia a livello di servizio che di metodo. I tag aiutano a raggruppare operazioni correlate, mentre le estensioni ti permettono di aggiungere metadati personalizzati alla tua specifica API.
Quando applicati a livello di servizio, i tag sono disponibili per tutti i metodi in quel servizio:
var _ = Service("UserService", func() {
// Definisci tag per l'intero servizio
HTTP(func() {
// Aggiungi un tag semplice
Meta("openapi:tag:Users")
// Aggiungi un tag con descrizione
Meta("openapi:tag:Backend:desc", "Operazioni API Backend")
// Aggiungi URL della documentazione al tag
Meta("openapi:tag:Backend:url", "http://example.com/docs")
Meta("openapi:tag:Backend:url:desc", "Documentazione API")
})
// Tutti i metodi in questo servizio erediteranno questi tag
Method("CreateUser", func() {
HTTP(func() {
POST("/users")
})
})
Method("ListUsers", func() {
HTTP(func() {
GET("/users")
})
})
})
Puoi anche applicare tag a metodi specifici, sia aggiungendo che sovrascrivendo i tag a livello di servizio:
var _ = Service("UserService", func() {
Method("AdminOperation", func() {
HTTP(func() {
// Aggiungi tag aggiuntivi solo per questo metodo
Meta("openapi:tag:Admin")
Meta("openapi:tag:Admin:desc", "Operazioni Amministrative")
POST("/admin/users")
})
})
})
Le estensioni possono essere aggiunte a più livelli per personalizzare diverse parti della specifica OpenAPI:
var _ = API("MyAPI", func() {
// Estensione a livello di API
Meta("openapi:extension:x-api-version", `"1.0"`)
})
var _ = Service("UserService", func() {
// Estensione a livello di servizio
HTTP(func() {
Meta("openapi:extension:x-service-class", `"premium"`)
})
Method("CreateUser", func() {
// Estensione a livello di metodo
HTTP(func() {
Meta("openapi:extension:x-rate-limit", `{"rate": 100, "burst": 200}`)
POST("/users")
})
})
})
Questo genera la seguente specifica OpenAPI:
{
"info": {
"x-api-version": "1.0"
},
"tags": [
{
"name": "Users",
"description": "Operazioni di gestione utenti"
},
{
"name": "Backend",
"description": "Operazioni API Backend",
"externalDocs": {
"description": "Documentazione API",
"url": "http://example.com/docs"
}
},
{
"name": "Admin",
"description": "Operazioni Amministrative"
}
],
"paths": {
"/users": {
"post": {
"tags": ["Users", "Backend"],
"x-service-class": "premium",
"x-rate-limit": {
"rate": 100,
"burst": 200
}
},
"get": {
"tags": ["Users", "Backend"]
}
},
"/admin/users": {
"post": {
"tags": ["Users", "Backend", "Admin"],
"x-service-class": "premium"
}
}
}
}