Exemplo Go
Este documento contém um exemplo completo de implementação da API Flowbiz em Go.
Código Completo
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"time"
)
// Configuração
const (
API_KEY = "sua_api_key_aqui" // Em produção, use variáveis de ambiente
PORT = 8080
)
// Estruturas de dados (JSON camelCase conforme manual)
type Brand struct {
Id string `json:"id"`
Name string `json:"name"`
}
type ProductCategory struct {
Id string `json:"id"`
Name string `json:"name"`
}
type Variant struct {
Sku string `json:"sku"`
Name string `json:"name"`
Price float64 `json:"price"`
Stock int `json:"stock"`
ImageUrl string `json:"imageUrl,omitempty"`
Url string `json:"url,omitempty"`
Available bool `json:"available"`
}
type Product struct {
ProductId string `json:"productId"`
Url string `json:"url"`
Brand Brand `json:"brand"`
Category []ProductCategory `json:"category"`
Variants []Variant `json:"variants"`
}
type OrderItem struct {
ProductId string `json:"productId"`
Categories []ProductCategory `json:"categories"`
Sku string `json:"sku"`
Name string `json:"name"`
Brand string `json:"brand"`
Price float64 `json:"price"`
Quantity int `json:"quantity"`
Url string `json:"url,omitempty"`
ImageUrl string `json:"imageUrl,omitempty"`
}
type PaymentMethod struct {
Type string `json:"type"`
Amount float64 `json:"amount"`
}
type DeliveryMethod struct {
Type string `json:"type"`
Amount float64 `json:"amount"`
}
type DeliveryAddress struct {
City string `json:"city"`
AddressLine2 string `json:"addressLine2,omitempty"`
Neighborhood string `json:"neighborhood,omitempty"`
AddressNumber string `json:"addressNumber,omitempty"`
State string `json:"state"`
AddressLine1 string `json:"addressLine1"`
PostalCode string `json:"postalCode"`
Country string `json:"country,omitempty"`
}
type Order struct {
Platform string `json:"platform"`
OrderId string `json:"orderId"`
Date time.Time `json:"date"`
Subtotal float64 `json:"subtotal"`
Freight float64 `json:"freight"`
Discounts float64 `json:"discounts"`
Total float64 `json:"total"`
Currency string `json:"currency"`
RawPaymentStatus string `json:"rawPaymentStatus"`
IsPaid bool `json:"isPaid"`
CustomerId string `json:"customerId"`
CustomerEmail string `json:"customerEmail"`
Items []OrderItem `json:"items"`
PaymentMethods []PaymentMethod `json:"paymentMethods,omitempty"`
DeliveryMethods []DeliveryMethod `json:"deliveryMethods,omitempty"`
DeliveryAddress DeliveryAddress `json:"deliveryAddress,omitempty"`
}
// Middleware para validar a API Key
func validateAPIKey(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
apiKey := r.Header.Get("X-Impulse-Key")
if apiKey != API_KEY {
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "unauthorized", "message": "X-Impulse-Key is missing or invalid"})
return
}
next.ServeHTTP(w, r)
})
}
// Função auxiliar para obter parâmetros de paginação
func getPaginationParams(r *http.Request) (int, int) {
offset := 0
limit := 50
if offsetStr := r.URL.Query().Get("offset"); offsetStr != "" {
if val, err := strconv.Atoi(offsetStr); err == nil {
offset = val
}
}
if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
if val, err := strconv.Atoi(limitStr); err == nil {
limit = val
}
}
if limit <= 0 {
limit = 50
}
if limit > 100 {
limit = 100
}
if offset < 0 {
offset = 0
}
return offset, limit
}
// Função auxiliar para obter e validar filtro de data (YYYY-MM-DD)
func getDateFilter(r *http.Request) (time.Time, time.Time, bool) {
start := r.URL.Query().Get("start_date")
end := r.URL.Query().Get("end_date")
if start == "" || end == "" {
return time.Time{}, time.Time{}, false
}
// Inclusivo: dia inteiro
startDate, err1 := time.Parse(time.RFC3339, start+"T00:00:00Z")
endDate, err2 := time.Parse(time.RFC3339, end+"T23:59:59Z")
if err1 != nil || err2 != nil {
return time.Time{}, time.Time{}, false
}
return startDate, endDate, true
}
// Função para responder com envelope de paginação
func respondPaginated(w http.ResponseWriter, items interface{}, total, offset, limit, count int) {
hasNext := (offset + count) < total
resp := map[string]interface{}{
"data": items,
"total": total,
"offset": offset,
"limit": limit,
"count": count,
"hasNext": hasNext,
}
if hasNext {
resp["nextOffset"] = offset + count
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
// Handlers para os endpoints
func categoriesHandler(w http.ResponseWriter, r *http.Request) {
offset, limit := getPaginationParams(r)
all := []ProductCategory{
{Id: "1", Name: "Eletrônicos"},
{Id: "2", Name: "Roupas"},
{Id: "3", Name: "Acessórios"},
}
total := len(all)
end := offset + limit
if end > total {
end = total
}
var page []ProductCategory
if offset >= total {
page = []ProductCategory{}
} else {
page = all[offset:end]
}
respondPaginated(w, page, total, offset, limit, len(page))
}
func brandsHandler(w http.ResponseWriter, r *http.Request) {
offset, limit := getPaginationParams(r)
all := []Brand{
{Id: "1", Name: "Samsung"},
{Id: "2", Name: "Apple"},
{Id: "3", Name: "Nike"},
}
total := len(all)
end := offset + limit
if end > total {
end = total
}
var page []Brand
if offset >= total {
page = []Brand{}
} else {
page = all[offset:end]
}
respondPaginated(w, page, total, offset, limit, len(page))
}
func productsHandler(w http.ResponseWriter, r *http.Request) {
offset, limit := getPaginationParams(r)
products := []Product{
{
ProductId: "1",
Url: "https://exemplo.com/produto/1",
Brand: Brand{Id: "1", Name: "Samsung"},
Category: []ProductCategory{{Id: "1", Name: "Eletrônicos"}},
Variants: []Variant{{Sku: "SKU-1A", Name: "Variante A", Price: 50.0, Stock: 10, Available: true}},
},
{
ProductId: "2",
Url: "https://exemplo.com/produto/2",
Brand: Brand{Id: "2", Name: "Apple"},
Category: []ProductCategory{{Id: "1", Name: "Eletrônicos"}},
Variants: []Variant{{Sku: "SKU-2A", Name: "Variante A", Price: 75.0, Stock: 5, Available: true}},
},
}
total := len(products)
end := offset + limit
if end > total {
end = total
}
var page []Product
if offset >= total {
page = []Product{}
} else {
page = products[offset:end]
}
respondPaginated(w, page, total, offset, limit, len(page))
}
func ordersHandler(w http.ResponseWriter, r *http.Request) {
offset, limit := getPaginationParams(r)
startDate, endDate, hasDate := getDateFilter(r)
// Simulação de dados
all := []Order{
{
Platform: "SuaPlataforma",
OrderId: "12345",
Date: time.Date(2023, 6, 1, 10, 0, 0, 0, time.UTC),
Subtotal: 100.0,
Freight: 10.0,
Discounts: 5.0,
Total: 105.0,
Currency: "BRL",
RawPaymentStatus: "Pago",
IsPaid: true,
CustomerId: "1",
CustomerEmail: "[email protected]",
Items: []OrderItem{
{
ProductId: "1",
Categories: []ProductCategory{{Id: "1", Name: "Eletrônicos"}},
Sku: "SKU-1A",
Name: "Produto 1",
Brand: "Samsung",
Price: 50.0,
Quantity: 2,
Url: "https://exemplo.com/produto/1",
ImageUrl: "https://exemplo.com/img/1.jpg",
},
},
PaymentMethods: []PaymentMethod{{Type: "credit_card", Amount: 105.0}},
DeliveryMethods: []DeliveryMethod{{Type: "standard", Amount: 10.0}},
DeliveryAddress: DeliveryAddress{
City: "São Paulo",
AddressLine2: "Apto 101",
Neighborhood: "Centro",
AddressNumber: "123",
State: "SP",
AddressLine1: "Rua Principal",
PostalCode: "01000-000",
Country: "BR",
},
},
}
// Validar filtro obrigatório
if !hasDate {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": "bad_request", "message": "start_date and end_date are required in YYYY-MM-DD format"})
return
}
// Filtrar por intervalo inclusivo
filtered := make([]Order, 0)
for _, o := range all {
if (o.Date.Equal(startDate) || o.Date.After(startDate)) && (o.Date.Equal(endDate) || o.Date.Before(endDate)) {
filtered = append(filtered, o)
}
}
total := len(filtered)
end := offset + limit
if end > total {
end = total
}
var page []Order
if offset >= total {
page = []Order{}
} else {
page = filtered[offset:end]
}
respondPaginated(w, page, total, offset, limit, len(page))
}
func main() {
// Criar router
mux := http.NewServeMux()
// Aplicar middleware de validação da API Key
apiKeyMiddleware := validateAPIKey
// Registrar handlers
mux.HandleFunc("/api/v2/healthz", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
})
mux.Handle("/api/v2/categories", apiKeyMiddleware(http.HandlerFunc(categoriesHandler)))
mux.Handle("/api/v2/brands", apiKeyMiddleware(http.HandlerFunc(brandsHandler)))
mux.Handle("/api/v2/products", apiKeyMiddleware(http.HandlerFunc(productsHandler)))
mux.Handle("/api/v2/orders", apiKeyMiddleware(http.HandlerFunc(ordersHandler)))
// Iniciar servidor
serverAddr := fmt.Sprintf(":%d", PORT)
log.Printf("Servidor iniciado na porta %d\n", PORT)
log.Fatal(http.ListenAndServe(serverAddr, mux))
}Explicação
Este exemplo implementa uma API REST em Go que atende aos requisitos do padrão Flowbiz. A implementação inclui:
- Estruturas de dados para representar os objetos do domínio (Order, Product, etc.)
- Middleware de autenticação para validar a API Key
- Endpoints para listar categorias, marcas, produtos e pedidos
- Suporte à paginação em todos os endpoints
- Filtro de data no endpoint de pedidos
Para usar este exemplo, você precisará:
- Substituir
sua_api_key_aquipela sua chave de API real - Implementar a lógica para buscar dados do seu banco de dados ou sistema de armazenamento
- Adaptar as estruturas de dados conforme necessário para seu caso de uso específico
Executando o Exemplo
Para executar este exemplo:
go run main.goO servidor será iniciado na porta 8080 e estará pronto para receber requisições.
Updated 3 months ago
