426 lines
11 KiB
Bash
426 lines
11 KiB
Bash
|
|
#!/bin/bash
|
|||
|
|
|
|||
|
|
# 🎯 GÉNÉRATEUR DE FEATURE - VEZA
|
|||
|
|
# Script pour générer une nouvelle feature complète
|
|||
|
|
|
|||
|
|
set -e
|
|||
|
|
|
|||
|
|
# Couleurs
|
|||
|
|
GREEN='\033[0;32m'
|
|||
|
|
BLUE='\033[0;34m'
|
|||
|
|
YELLOW='\033[1;33m'
|
|||
|
|
NC='\033[0m'
|
|||
|
|
|
|||
|
|
# Fonction pour afficher les messages
|
|||
|
|
log_info() {
|
|||
|
|
echo -e "${BLUE}ℹ️ $1${NC}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
log_success() {
|
|||
|
|
echo -e "${GREEN}✅ $1${NC}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
log_warning() {
|
|||
|
|
echo -e "${YELLOW}⚠️ $1${NC}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Vérification des arguments
|
|||
|
|
if [ $# -eq 0 ]; then
|
|||
|
|
echo "Usage: $0 <feature-name>"
|
|||
|
|
echo "Exemple: $0 user-authentication"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
FEATURE_NAME=$1
|
|||
|
|
FEATURE_DIR="features/feature-$(date +%Y%m%d)-${FEATURE_NAME}"
|
|||
|
|
|
|||
|
|
log_info "Génération de la feature: $FEATURE_NAME"
|
|||
|
|
|
|||
|
|
# Création de la structure de la feature
|
|||
|
|
mkdir -p "$FEATURE_DIR"/{backend,frontend,mobile,tests,docs}
|
|||
|
|
|
|||
|
|
log_success "Structure de répertoires créée"
|
|||
|
|
|
|||
|
|
# Génération du backend (Go)
|
|||
|
|
log_info "Génération du code backend..."
|
|||
|
|
|
|||
|
|
cat > "$FEATURE_DIR/backend/${FEATURE_NAME}_service.go" << 'GOEOF'
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"fmt"
|
|||
|
|
"time"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// ${FEATURE_NAME^}Service gère les fonctionnalités de ${FEATURE_NAME}
|
|||
|
|
type ${FEATURE_NAME^}Service struct {
|
|||
|
|
// Ajoutez vos dépendances ici
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// New${FEATURE_NAME^}Service crée une nouvelle instance du service
|
|||
|
|
func New${FEATURE_NAME^}Service() *${FEATURE_NAME^}Service {
|
|||
|
|
return &${FEATURE_NAME^}Service{}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Exemple de méthode - remplacez par vos méthodes
|
|||
|
|
func (s *${FEATURE_NAME^}Service) Process${FEATURE_NAME^}(ctx context.Context, input string) (string, error) {
|
|||
|
|
// Implémentation de votre logique métier
|
|||
|
|
return fmt.Sprintf("Processed: %s", input), nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Health check pour le service
|
|||
|
|
func (s *${FEATURE_NAME^}Service) HealthCheck() error {
|
|||
|
|
// Implémentation du health check
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
GOEOF
|
|||
|
|
|
|||
|
|
cat > "$FEATURE_DIR/backend/${FEATURE_NAME}_handler.go" << 'GOEOF'
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"net/http"
|
|||
|
|
"github.com/gin-gonic/gin"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// ${FEATURE_NAME^}Handler gère les endpoints HTTP pour ${FEATURE_NAME}
|
|||
|
|
type ${FEATURE_NAME^}Handler struct {
|
|||
|
|
service *${FEATURE_NAME^}Service
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// New${FEATURE_NAME^}Handler crée un nouveau handler
|
|||
|
|
func New${FEATURE_NAME^}Handler(service *${FEATURE_NAME^}Service) *${FEATURE_NAME^}Handler {
|
|||
|
|
return &${FEATURE_NAME^}Handler{
|
|||
|
|
service: service,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SetupRoutes configure les routes pour cette feature
|
|||
|
|
func (h *${FEATURE_NAME^}Handler) SetupRoutes(r *gin.RouterGroup) {
|
|||
|
|
r.GET("/${FEATURE_NAME}", h.Get${FEATURE_NAME^})
|
|||
|
|
r.POST("/${FEATURE_NAME}", h.Create${FEATURE_NAME^})
|
|||
|
|
r.PUT("/${FEATURE_NAME}/:id", h.Update${FEATURE_NAME^})
|
|||
|
|
r.DELETE("/${FEATURE_NAME}/:id", h.Delete${FEATURE_NAME^})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Get${FEATURE_NAME^} récupère les données de ${FEATURE_NAME}
|
|||
|
|
func (h *${FEATURE_NAME^}Handler) Get${FEATURE_NAME^}(c *gin.Context) {
|
|||
|
|
// Implémentation
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"message": "Get ${FEATURE_NAME} endpoint"})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Create${FEATURE_NAME^} crée une nouvelle entrée
|
|||
|
|
func (h *${FEATURE_NAME^}Handler) Create${FEATURE_NAME^}(c *gin.Context) {
|
|||
|
|
// Implémentation
|
|||
|
|
c.JSON(http.StatusCreated, gin.H{"message": "Create ${FEATURE_NAME} endpoint"})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Update${FEATURE_NAME^} met à jour une entrée existante
|
|||
|
|
func (h *${FEATURE_NAME^}Handler) Update${FEATURE_NAME^}(c *gin.Context) {
|
|||
|
|
// Implémentation
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"message": "Update ${FEATURE_NAME} endpoint"})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Delete${FEATURE_NAME^} supprime une entrée
|
|||
|
|
func (h *${FEATURE_NAME^}Handler) Delete${FEATURE_NAME^}(c *gin.Context) {
|
|||
|
|
// Implémentation
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"message": "Delete ${FEATURE_NAME} endpoint"})
|
|||
|
|
}
|
|||
|
|
GOEOF
|
|||
|
|
|
|||
|
|
log_success "Code backend généré"
|
|||
|
|
|
|||
|
|
# Génération du frontend (React)
|
|||
|
|
log_info "Génération du code frontend..."
|
|||
|
|
|
|||
|
|
cat > "$FEATURE_DIR/frontend/${FEATURE_NAME^}Page.tsx" << 'REACTEOF'
|
|||
|
|
import React, { useState, useEffect } from 'react';
|
|||
|
|
import { Card, Button, Input, Modal } from '../components/DesignSystem';
|
|||
|
|
|
|||
|
|
interface ${FEATURE_NAME^}Data {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
// Ajoutez vos propriétés ici
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const ${FEATURE_NAME^}Page: React.FC = () => {
|
|||
|
|
const [data, setData] = useState<${FEATURE_NAME^}Data[]>([]);
|
|||
|
|
const [loading, setLoading] = useState(false);
|
|||
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
fetch${FEATURE_NAME^}Data();
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
const fetch${FEATURE_NAME^}Data = async () => {
|
|||
|
|
setLoading(true);
|
|||
|
|
try {
|
|||
|
|
// Implémentation de la récupération des données
|
|||
|
|
const response = await fetch('/api/${FEATURE_NAME}');
|
|||
|
|
const result = await response.json();
|
|||
|
|
setData(result);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Erreur lors de la récupération des données:', error);
|
|||
|
|
} finally {
|
|||
|
|
setLoading(false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleCreate = async (formData: any) => {
|
|||
|
|
try {
|
|||
|
|
// Implémentation de la création
|
|||
|
|
const response = await fetch('/api/${FEATURE_NAME}', {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: { 'Content-Type': 'application/json' },
|
|||
|
|
body: JSON.stringify(formData),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (response.ok) {
|
|||
|
|
fetch${FEATURE_NAME^}Data();
|
|||
|
|
setIsModalOpen(false);
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Erreur lors de la création:', error);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="p-6">
|
|||
|
|
<div className="flex justify-between items-center mb-6">
|
|||
|
|
<h1 className="text-3xl font-bold text-white">${FEATURE_NAME^}</h1>
|
|||
|
|
<Button onClick={() => setIsModalOpen(true)}>
|
|||
|
|
Créer ${FEATURE_NAME^}
|
|||
|
|
</Button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|||
|
|
{data.map((item) => (
|
|||
|
|
<Card key={item.id} className="p-4">
|
|||
|
|
<h3 className="text-lg font-semibold text-white">{item.name}</h3>
|
|||
|
|
{/* Ajoutez vos éléments d'affichage ici */}
|
|||
|
|
</Card>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<Modal
|
|||
|
|
isOpen={isModalOpen}
|
|||
|
|
onClose={() => setIsModalOpen(false)}
|
|||
|
|
title="Créer ${FEATURE_NAME^}"
|
|||
|
|
>
|
|||
|
|
{/* Ajoutez votre formulaire ici */}
|
|||
|
|
<form onSubmit={(e) => {
|
|||
|
|
e.preventDefault();
|
|||
|
|
// Implémentation de la soumission
|
|||
|
|
}}>
|
|||
|
|
<Input
|
|||
|
|
type="text"
|
|||
|
|
placeholder="Nom"
|
|||
|
|
required
|
|||
|
|
/>
|
|||
|
|
<Button type="submit" className="mt-4">
|
|||
|
|
Créer
|
|||
|
|
</Button>
|
|||
|
|
</form>
|
|||
|
|
</Modal>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default ${FEATURE_NAME^}Page;
|
|||
|
|
REACTEOF
|
|||
|
|
|
|||
|
|
log_success "Code frontend généré"
|
|||
|
|
|
|||
|
|
# Génération des tests
|
|||
|
|
log_info "Génération des tests..."
|
|||
|
|
|
|||
|
|
cat > "$FEATURE_DIR/tests/${FEATURE_NAME}_test.go" << 'TESTEOF'
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"testing"
|
|||
|
|
"context"
|
|||
|
|
"github.com/stretchr/testify/assert"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func Test${FEATURE_NAME^}Service(t *testing.T) {
|
|||
|
|
service := New${FEATURE_NAME^}Service()
|
|||
|
|
|
|||
|
|
t.Run("should process ${FEATURE_NAME} successfully", func(t *testing.T) {
|
|||
|
|
ctx := context.Background()
|
|||
|
|
input := "test input"
|
|||
|
|
|
|||
|
|
result, err := service.Process${FEATURE_NAME^}(ctx, input)
|
|||
|
|
|
|||
|
|
assert.NoError(t, err)
|
|||
|
|
assert.Contains(t, result, input)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
t.Run("should pass health check", func(t *testing.T) {
|
|||
|
|
err := service.HealthCheck()
|
|||
|
|
assert.NoError(t, err)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
TESTEOF
|
|||
|
|
|
|||
|
|
cat > "$FEATURE_DIR/tests/${FEATURE_NAME}_integration_test.go" << 'INTEGEOF'
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"testing"
|
|||
|
|
"net/http"
|
|||
|
|
"net/http/httptest"
|
|||
|
|
"github.com/gin-gonic/gin"
|
|||
|
|
"github.com/stretchr/testify/assert"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func Test${FEATURE_NAME^}Endpoints(t *testing.T) {
|
|||
|
|
gin.SetMode(gin.TestMode)
|
|||
|
|
|
|||
|
|
service := New${FEATURE_NAME^}Service()
|
|||
|
|
handler := New${FEATURE_NAME^}Handler(service)
|
|||
|
|
|
|||
|
|
router := gin.New()
|
|||
|
|
handler.SetupRoutes(router.Group("/api"))
|
|||
|
|
|
|||
|
|
t.Run("GET /api/${FEATURE_NAME}", func(t *testing.T) {
|
|||
|
|
w := httptest.NewRecorder()
|
|||
|
|
req, _ := http.NewRequest("GET", "/api/${FEATURE_NAME}", nil)
|
|||
|
|
router.ServeHTTP(w, req)
|
|||
|
|
|
|||
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
t.Run("POST /api/${FEATURE_NAME}", func(t *testing.T) {
|
|||
|
|
w := httptest.NewRecorder()
|
|||
|
|
req, _ := http.NewRequest("POST", "/api/${FEATURE_NAME}", nil)
|
|||
|
|
router.ServeHTTP(w, req)
|
|||
|
|
|
|||
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
INTEGEOF
|
|||
|
|
|
|||
|
|
log_success "Tests générés"
|
|||
|
|
|
|||
|
|
# Génération de la documentation
|
|||
|
|
log_info "Génération de la documentation..."
|
|||
|
|
|
|||
|
|
cat > "$FEATURE_DIR/docs/README.md" << 'DOCEOF'
|
|||
|
|
# ${FEATURE_NAME^} Feature
|
|||
|
|
|
|||
|
|
## Description
|
|||
|
|
Cette feature implémente la fonctionnalité de ${FEATURE_NAME}.
|
|||
|
|
|
|||
|
|
## Composants
|
|||
|
|
|
|||
|
|
### Backend
|
|||
|
|
- `${FEATURE_NAME}_service.go` - Service principal
|
|||
|
|
- `${FEATURE_NAME}_handler.go` - Handlers HTTP
|
|||
|
|
|
|||
|
|
### Frontend
|
|||
|
|
- `${FEATURE_NAME^}Page.tsx` - Page React
|
|||
|
|
|
|||
|
|
### Tests
|
|||
|
|
- `${FEATURE_NAME}_test.go` - Tests unitaires
|
|||
|
|
- `${FEATURE_NAME}_integration_test.go` - Tests d'intégration
|
|||
|
|
|
|||
|
|
## API Endpoints
|
|||
|
|
|
|||
|
|
### GET /api/${FEATURE_NAME}
|
|||
|
|
Récupère la liste des ${FEATURE_NAME}.
|
|||
|
|
|
|||
|
|
### POST /api/${FEATURE_NAME}
|
|||
|
|
Crée une nouvelle entrée ${FEATURE_NAME}.
|
|||
|
|
|
|||
|
|
### PUT /api/${FEATURE_NAME}/:id
|
|||
|
|
Met à jour une entrée ${FEATURE_NAME} existante.
|
|||
|
|
|
|||
|
|
### DELETE /api/${FEATURE_NAME}/:id
|
|||
|
|
Supprime une entrée ${FEATURE_NAME}.
|
|||
|
|
|
|||
|
|
## Utilisation
|
|||
|
|
|
|||
|
|
1. Implémentez la logique métier dans le service
|
|||
|
|
2. Ajoutez les validations nécessaires
|
|||
|
|
3. Implémentez les tests
|
|||
|
|
4. Intégrez dans l'application principale
|
|||
|
|
|
|||
|
|
## Tests
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Tests unitaires
|
|||
|
|
go test ./features/feature-$(date +%Y%m%d)-${FEATURE_NAME}/tests/
|
|||
|
|
|
|||
|
|
# Tests d'intégration
|
|||
|
|
go test -tags=integration ./features/feature-$(date +%Y%m%d)-${FEATURE_NAME}/tests/
|
|||
|
|
```
|
|||
|
|
DOCEOF
|
|||
|
|
|
|||
|
|
log_success "Documentation générée"
|
|||
|
|
|
|||
|
|
# Génération du fichier de configuration
|
|||
|
|
cat > "$FEATURE_DIR/feature-config.yaml" << 'CONFIGEOF'
|
|||
|
|
feature:
|
|||
|
|
name: ${FEATURE_NAME}
|
|||
|
|
version: 1.0.0
|
|||
|
|
description: "Feature ${FEATURE_NAME} implementation"
|
|||
|
|
|
|||
|
|
components:
|
|||
|
|
backend:
|
|||
|
|
service: true
|
|||
|
|
handler: true
|
|||
|
|
repository: false
|
|||
|
|
middleware: false
|
|||
|
|
|
|||
|
|
frontend:
|
|||
|
|
page: true
|
|||
|
|
component: false
|
|||
|
|
hook: false
|
|||
|
|
|
|||
|
|
mobile:
|
|||
|
|
screen: false
|
|||
|
|
component: false
|
|||
|
|
|
|||
|
|
tests:
|
|||
|
|
unit: true
|
|||
|
|
integration: true
|
|||
|
|
e2e: false
|
|||
|
|
|
|||
|
|
docs:
|
|||
|
|
readme: true
|
|||
|
|
api: true
|
|||
|
|
user_guide: false
|
|||
|
|
|
|||
|
|
dependencies:
|
|||
|
|
backend:
|
|||
|
|
- gin
|
|||
|
|
- context
|
|||
|
|
frontend:
|
|||
|
|
- react
|
|||
|
|
- typescript
|
|||
|
|
mobile:
|
|||
|
|
- react-native
|
|||
|
|
- expo
|
|||
|
|
CONFIGEOF
|
|||
|
|
|
|||
|
|
log_success "Configuration générée"
|
|||
|
|
|
|||
|
|
# Résumé
|
|||
|
|
echo -e "${GREEN}"
|
|||
|
|
echo "╔══════════════════════════════════════════════════════════════╗"
|
|||
|
|
echo "║ FEATURE GÉNÉRÉE AVEC SUCCÈS ║"
|
|||
|
|
echo "╚══════════════════════════════════════════════════════════════╝"
|
|||
|
|
echo -e "${NC}"
|
|||
|
|
|
|||
|
|
echo -e "${BLUE}📁 Feature créée dans: $FEATURE_DIR${NC}"
|
|||
|
|
echo -e "${BLUE}📋 Prochaines étapes:${NC}"
|
|||
|
|
echo "1. Implémentez la logique métier dans les services"
|
|||
|
|
echo "2. Ajoutez les validations et la gestion d'erreurs"
|
|||
|
|
echo "3. Implémentez les tests"
|
|||
|
|
echo "4. Intégrez dans l'application principale"
|
|||
|
|
echo "5. Mettez à jour la documentation"
|
|||
|
|
|
|||
|
|
log_success "Feature $FEATURE_NAME générée avec succès!"
|