first commit

This commit is contained in:
Blas Santome Ocampo
2026-03-23 09:35:15 +01:00
commit 5ff88d8cf6
17 changed files with 1292 additions and 0 deletions

28
.gitignore vendored Normal file
View File

@@ -0,0 +1,28 @@
# Dependencias
node_modules/
package-lock.json
# Archivos temporales de generación
.temp-generated/
temp-generated/
# Reportes
generation-report.json
# Logs
*.log
npm-debug.log*
# Sistema operativo
.DS_Store
Thumbs.db
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# Output de prueba
test-output/

33
.openapi-generator-ignore Normal file
View File

@@ -0,0 +1,33 @@
# OpenAPI Generator Ignore File
# Archivos que no queremos generar
# Documentación
README.md
.gitignore
git_push.sh
# NPM
.npmignore
package.json
package-lock.json
# TypeScript config
tsconfig.json
tsconfig.spec.json
# Angular specific
angular.json
karma.conf.js
# Tests que no usamos
*.spec.ts
# Variables de entorno
variables.ts
configuration.ts
# Encoder
encoder.ts
# API base que usamos nuestra propia implementación
api.ts

157
QUICKSTART.md Normal file
View File

@@ -0,0 +1,157 @@
# 🚀 Guía de Inicio Rápido
## Instalación en 3 pasos
### 1. Instalar dependencias
```bash
npm install
```
### 2. Instalar OpenAPI Generator CLI
```bash
npm run setup
# O manualmente:
npm install -g @openapitools/openapi-generator-cli
```
### 3. Probar con el ejemplo incluido
```bash
node generate.js -i example-swagger.yaml -o ./test-output --dry-run
```
## Uso con tu API
### Paso 1: Copia tu archivo Swagger/OpenAPI
```bash
cp /ruta/a/tu/api.yaml ./swagger.yaml
```
### Paso 2: Genera el código
```bash
node generate.js -i swagger.yaml -o ./src/app
```
### Paso 3: Revisa los archivos generados
```bash
ls -la ./src/app/data/
ls -la ./src/app/domain/
ls -la ./src/app/di/
```
## Opciones Comunes
### Generar en otra carpeta
```bash
node generate.js -i swagger.yaml -o ./frontend/src/app
```
### Usar templates personalizados
```bash
# Edita los archivos en ./templates/
# Luego ejecuta:
node generate.js -i swagger.yaml -t ./templates
```
### Modo de prueba (sin generar archivos)
```bash
node generate.js -i swagger.yaml --dry-run
```
## Integración con tu proyecto Angular
### 1. Registra los providers
En `app.config.ts` o `app.module.ts`:
```typescript
import { UserRepositoryProvider } from '@/di/repositories/user.repository.provider';
import { UserUseCasesProvider } from '@/di/use-cases/user.use-cases.provider';
export const appConfig: ApplicationConfig = {
providers: [
// ... otros providers
UserRepositoryProvider,
UserUseCasesProvider
]
};
```
### 2. Configura los path aliases
En `tsconfig.json`:
```json
{
"compilerOptions": {
"paths": {
"@/*": ["src/app/*"],
"@environment": ["src/environments/environment"]
}
}
}
```
### 3. Usa en tus componentes
```typescript
import { Component, inject } from '@angular/core';
import { USER_USE_CASES } from '@/domain/use-cases/user/user.use-cases.contract';
@Component({
selector: 'app-users',
template: `...`
})
export class UsersComponent {
#userUseCases = inject(USER_USE_CASES);
ngOnInit() {
this.#userUseCases.getUsers().subscribe(users => {
console.log(users);
});
}
}
```
## Troubleshooting
### ❌ Error: openapi-generator-cli: command not found
**Solución:**
```bash
npm install -g @openapitools/openapi-generator-cli
```
### ❌ Error: Cannot find module 'commander'
**Solución:**
```bash
npm install
```
### ❌ Los archivos no se generan
**Solución:** Verifica que el directorio de salida existe o usa `--dry-run` para ver qué pasaría:
```bash
node generate.js --dry-run
```
## Próximos pasos
1. ✅ Genera el código desde tu Swagger
2. 📝 Ajusta los templates según tus necesidades
3. 🔧 Configura los path aliases en tu tsconfig.json
4. 📦 Registra los providers en tu módulo Angular
5. 🚀 ¡Usa el código generado en tus componentes!
## ¿Necesitas ayuda?
Consulta el README.md completo para documentación detallada.

286
README.md Normal file
View File

@@ -0,0 +1,286 @@
# OpenAPI Clean Architecture Generator
Generador de código Angular con Clean Architecture desde archivos OpenAPI/Swagger.
## 🚀 Instalación
### Opción 1: Instalación Global
```bash
npm install -g @openapitools/openapi-generator-cli
npm install
```
### Opción 2: Usar directamente
```bash
npm install
npm run setup
```
## 📖 Uso
### Comando básico
```bash
node generate.js -i swagger.yaml
```
### Opciones disponibles
```bash
node generate.js [opciones]
Opciones:
-V, --version Mostrar versión
-i, --input <file> Archivo OpenAPI/Swagger (yaml o json) [default: swagger.yaml]
-o, --output <dir> Directorio de salida [default: ./src/app]
-t, --templates <dir> Directorio de templates personalizados [default: ./templates]
--skip-install No instalar dependencias
--dry-run Simular sin generar archivos
-h, --help Mostrar ayuda
```
### Ejemplos
```bash
# Generar desde swagger.yaml en src/app
node generate.js -i swagger.yaml -o ./src/app
# Usar templates personalizados
node generate.js -i api.yaml -t ./mis-templates
# Modo de prueba (no genera archivos)
node generate.js -i swagger.yaml --dry-run
# Especificar todos los parámetros
node generate.js -i ./docs/api.yaml -o ./frontend/src/app -t ./custom-templates
```
## 📁 Estructura Generada
El generador crea la siguiente estructura siguiendo Clean Architecture:
```
src/app/
├── data/ # Capa de datos
│ ├── dtos/ # Data Transfer Objects
│ │ ├── node/
│ │ │ └── node.dto.ts
│ │ ├── order-type/
│ │ │ └── order-type.dto.ts
│ │ └── supply-mode/
│ │ └── supply-mode.dto.ts
│ ├── repositories/ # Implementaciones de repositorios
│ │ ├── node.repository.impl.ts
│ │ ├── order-type.repository.impl.ts
│ │ └── supply-mode.repository.impl.ts
│ └── mappers/ # Transformadores DTO → Entidad
│ ├── node.mapper.ts
│ ├── order-type.mapper.ts
│ └── supply-mode.mapper.ts
├── domain/ # Capa de dominio
│ ├── repositories/ # Contratos de repositorios
│ │ ├── node.repository.contract.ts
│ │ ├── order-type.repository.contract.ts
│ │ └── supply-mode.repository.contract.ts
│ └── use-cases/ # Casos de uso
│ ├── node/
│ │ ├── node.use-cases.contract.ts
│ │ └── node.use-cases.impl.ts
│ ├── order-type/
│ │ ├── order-type.use-cases.contract.ts
│ │ └── order-type.use-cases.impl.ts
│ └── supply-mode/
│ ├── supply-mode.use-cases.contract.ts
│ └── supply-mode.use-cases.impl.ts
├── di/ # Inyección de dependencias
│ ├── repositories/ # Providers de repositorios
│ │ ├── node.repository.provider.ts
│ │ ├── order-type.repository.provider.ts
│ │ └── supply-mode.repository.provider.ts
│ └── use-cases/ # Providers de use cases
│ ├── node.use-cases.provider.ts
│ ├── order-type.use-cases.provider.ts
│ └── supply-mode.use-cases.provider.ts
└── entities/ # Entidades de dominio
└── models/
├── node.model.ts
├── order-type.model.ts
└── supply-mode.model.ts
```
## 🔧 Personalización
### Modificar Templates
Los templates están en la carpeta `templates/`. Cada archivo `.mustache` define cómo se genera un tipo de archivo.
Templates disponibles:
- `model.mustache` - DTOs
- `model-entity.mustache` - Entidades del modelo
- `mapper.mustache` - Mappers
- `api.repository.contract.mustache` - Contratos de repositorio
- `api.repository.impl.mustache` - Implementaciones de repositorio
- `api.use-cases.contract.mustache` - Contratos de use cases
- `api.use-cases.impl.mustache` - Implementaciones de use cases
- `repository.provider.mustache` - Providers de repositorio
- `use-cases.provider.mustache` - Providers de use cases
### Variables Mustache Disponibles
```mustache
{{classname}} - Nombre de la clase (ej: "OrderType")
{{classVarName}} - Nombre en camelCase (ej: "orderType")
{{classFilename}} - Nombre del archivo (ej: "order-type")
{{constantName}} - Constante (ej: "ORDER_TYPE")
{{description}} - Descripción del schema
{{httpMethod}} - Método HTTP (get, post, etc)
{{path}} - Path del endpoint
{{nickname}} - Nombre del método
{{allParams}} - Todos los parámetros
{{returnType}} - Tipo de retorno
{{vars}} - Variables del modelo
```
## 📊 Reporte de Generación
Después de cada generación, se crea un archivo `generation-report.json` con estadísticas:
```json
{
"timestamp": "2025-01-15T10:30:00.000Z",
"tags": 3,
"endpoints": 8,
"outputDirectory": "./src/app",
"structure": {
"dtos": 15,
"repositories": 9,
"mappers": 3,
"useCases": 6
}
}
```
## 🎯 Ejemplo Completo
### 1. Preparar tu proyecto
```bash
# Clonar o copiar el generador
cd mi-proyecto-angular
mkdir generator
cd generator
# Copiar archivos del generador aquí
```
### 2. Copiar tu Swagger
```bash
cp ../docs/api.yaml ./swagger.yaml
```
### 3. Generar código
```bash
node generate.js
```
### 4. Registrar providers en Angular
En tu `app.module.ts` o `app.config.ts`:
```typescript
import { NodeRepositoryProvider } from '@/di/repositories/node.repository.provider';
import { NodeUseCasesProvider } from '@/di/use-cases/node.use-cases.provider';
// ... importar otros providers
@NgModule({
providers: [
// Repositories
NodeRepositoryProvider,
OrderTypeRepositoryProvider,
SupplyModeRepositoryProvider,
// Use Cases
NodeUseCasesProvider,
OrderTypeUseCasesProvider,
SupplyModeUseCasesProvider
]
})
export class AppModule {}
```
### 5. Usar en componentes
```typescript
import { Component, inject } from '@angular/core';
import { NODE_USE_CASES, NodeUseCases } from '@/domain/use-cases/node/node.use-cases.contract';
@Component({
selector: 'app-nodes',
template: `...`
})
export class NodesComponent {
#nodeUseCases = inject(NODE_USE_CASES);
loadNodes() {
this.#nodeUseCases.getNodes('TI').subscribe(nodes => {
console.log(nodes);
});
}
}
```
## 🐛 Troubleshooting
### Error: openapi-generator-cli no encontrado
```bash
npm install -g @openapitools/openapi-generator-cli
# o
npm run setup
```
### Error: Archivo swagger.yaml no encontrado
Asegúrate de especificar la ruta correcta:
```bash
node generate.js -i ./ruta/a/tu/swagger.yaml
```
### Los imports no se resuelven (@/ no funciona)
Configura los path aliases en tu `tsconfig.json`:
```json
{
"compilerOptions": {
"paths": {
"@/*": ["src/app/*"],
"@environment": ["src/environments/environment"]
}
}
}
```
### Los templates no generan el código esperado
1. Verifica que tus templates están en `./templates/`
2. Revisa la sintaxis Mustache
3. Usa `--dry-run` para verificar sin generar archivos
## 📝 Notas
- El generador crea archivos `.ts`, no los compila
- Los providers deben registrarse manualmente en tu módulo Angular
- Asegúrate de tener configurado `@mercadona/common` o ajusta los imports en los templates
- El generador asume Angular 17+ con inject() function
## 🤝 Contribuir
Si encuentras bugs o mejoras, siéntete libre de modificar los templates y el script según tus necesidades.
## 📄 Licencia
MIT

150
example-swagger.yaml Normal file
View File

@@ -0,0 +1,150 @@
openapi: 3.0.1
info:
title: Example API
description: API de ejemplo para probar el generador
version: 1.0.0
tags:
- name: User
description: Operaciones de usuario
- name: Product
description: Operaciones de productos
paths:
/v1/users:
get:
tags:
- User
summary: Obtener lista de usuarios
operationId: getUsers
parameters:
- name: search
in: query
required: false
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/UserResponse'
post:
tags:
- User
summary: Crear usuario
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/UserSchema'
/v1/users/{id}:
get:
tags:
- User
summary: Obtener usuario por ID
operationId: getUserById
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/UserSchema'
delete:
tags:
- User
summary: Eliminar usuario
operationId: deleteUser
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'204':
description: No Content
/v1/products:
get:
tags:
- Product
summary: Obtener lista de productos
operationId: getProducts
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ProductResponse'
components:
schemas:
UserSchema:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Juan Pérez
email:
type: string
example: juan@example.com
active:
type: boolean
example: true
CreateUserRequest:
type: object
required:
- name
- email
properties:
name:
type: string
example: Juan Pérez
email:
type: string
example: juan@example.com
UserResponse:
type: object
properties:
users:
type: array
items:
$ref: '#/components/schemas/UserSchema'
ProductSchema:
type: object
properties:
id:
type: integer
example: 100
name:
type: string
example: Laptop HP
price:
type: number
format: float
example: 599.99
ProductResponse:
type: object
properties:
products:
type: array
items:
$ref: '#/components/schemas/ProductSchema'

311
generate.js Executable file
View File

@@ -0,0 +1,311 @@
#!/usr/bin/env node
const { execSync } = require('child_process');
const fs = require('fs-extra');
const path = require('path');
const yaml = require('js-yaml');
const { program } = require('commander');
// Colores para console (sin dependencias externas)
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
green: '\x1b[32m',
blue: '\x1b[34m',
yellow: '\x1b[33m',
red: '\x1b[31m',
cyan: '\x1b[36m'
};
function log(message, color = 'reset') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
function logSuccess(message) {
log(`${message}`, 'green');
}
function logInfo(message) {
log(` ${message}`, 'blue');
}
function logWarning(message) {
log(`⚠️ ${message}`, 'yellow');
}
function logError(message) {
log(`${message}`, 'red');
}
function logStep(message) {
log(`\n🚀 ${message}`, 'cyan');
}
// Configuración del CLI
program
.name('generate-clean-arch')
.description('Generador de código Angular con Clean Architecture desde OpenAPI/Swagger')
.version('1.0.0')
.option('-i, --input <file>', 'Archivo OpenAPI/Swagger (yaml o json)', 'swagger.yaml')
.option('-o, --output <dir>', 'Directorio de salida', './src/app')
.option('-t, --templates <dir>', 'Directorio de templates personalizados', './templates')
.option('--skip-install', 'No instalar dependencias')
.option('--dry-run', 'Simular sin generar archivos')
.parse(process.argv);
const options = program.opts();
// Validar que existe openapi-generator-cli
function checkOpenApiGenerator() {
try {
execSync('openapi-generator-cli version', { stdio: 'ignore' });
return true;
} catch (error) {
return false;
}
}
// Instalar openapi-generator-cli
function installOpenApiGenerator() {
logStep('Instalando @openapitools/openapi-generator-cli...');
try {
execSync('npm install -g @openapitools/openapi-generator-cli', { stdio: 'inherit' });
logSuccess('OpenAPI Generator CLI instalado correctamente');
} catch (error) {
logError('Error al instalar OpenAPI Generator CLI');
process.exit(1);
}
}
// Crear estructura de directorios
function createDirectoryStructure(baseDir) {
const dirs = [
path.join(baseDir, 'data/dtos'),
path.join(baseDir, 'data/repositories'),
path.join(baseDir, 'data/mappers'),
path.join(baseDir, 'domain/repositories'),
path.join(baseDir, 'domain/use-cases'),
path.join(baseDir, 'di/repositories'),
path.join(baseDir, 'di/use-cases'),
path.join(baseDir, 'entities/models')
];
dirs.forEach(dir => {
fs.ensureDirSync(dir);
});
logSuccess('Estructura de directorios creada');
}
// Analizar el swagger para extraer tags y dominios
function analyzeSwagger(swaggerFile) {
logStep('Analizando archivo OpenAPI...');
try {
const fileContent = fs.readFileSync(swaggerFile, 'utf8');
const swagger = yaml.load(fileContent);
const tags = swagger.tags || [];
const paths = swagger.paths || {};
logInfo(`Encontrados ${tags.length} tags en el API`);
logInfo(`Encontrados ${Object.keys(paths).length} endpoints`);
tags.forEach(tag => {
logInfo(` - ${tag.name}: ${tag.description || 'Sin descripción'}`);
});
return { tags, paths, swagger };
} catch (error) {
logError(`Error al leer el archivo Swagger: ${error.message}`);
process.exit(1);
}
}
// Generar código con OpenAPI Generator
function generateCode(swaggerFile, templatesDir) {
logStep('Generando código desde OpenAPI...');
const tempDir = path.join(process.cwd(), '.temp-generated');
// Limpiar directorio temporal
if (fs.existsSync(tempDir)) {
fs.removeSync(tempDir);
}
try {
const command = `openapi-generator-cli generate \
-i "${swaggerFile}" \
-g typescript-angular \
-t "${templatesDir}" \
-o "${tempDir}" \
--additional-properties=ngVersion=17.0.0,withInterfaces=true,providedInRoot=false,supportsES6=true,modelPropertyNaming=camelCase`;
execSync(command, { stdio: 'inherit' });
logSuccess('Código generado correctamente');
return tempDir;
} catch (error) {
logError('Error al generar código');
if (fs.existsSync(tempDir)) {
fs.removeSync(tempDir);
}
process.exit(1);
}
}
// Organizar archivos según Clean Architecture
function organizeFiles(tempDir, outputDir) {
logStep('Organizando archivos en estructura Clean Architecture...');
const moves = [
{ from: 'model', to: path.join(outputDir, 'data/dtos'), pattern: '*.dto.ts' },
{ from: 'model', to: path.join(outputDir, 'entities/models'), pattern: '*.model.ts' },
{ from: 'api', to: path.join(outputDir, 'domain/repositories'), pattern: '*.repository.contract.ts' },
{ from: 'api', to: path.join(outputDir, 'data/repositories'), pattern: '*.repository.impl.ts' },
{ from: 'api', to: path.join(outputDir, 'data/repositories'), pattern: '*.repository.mock.ts' },
{ from: 'api', to: path.join(outputDir, 'data/mappers'), pattern: '*.mapper.ts' },
{ from: 'api', to: path.join(outputDir, 'domain/use-cases'), pattern: '*.use-cases.contract.ts' },
{ from: 'api', to: path.join(outputDir, 'domain/use-cases'), pattern: '*.use-cases.impl.ts' },
{ from: 'providers', to: path.join(outputDir, 'di/repositories'), pattern: '*.repository.provider.ts' },
{ from: 'providers', to: path.join(outputDir, 'di/use-cases'), pattern: '*.use-cases.provider.ts' }
];
let filesMoved = 0;
moves.forEach(({ from, to, pattern }) => {
const sourceDir = path.join(tempDir, from);
if (fs.existsSync(sourceDir)) {
fs.ensureDirSync(to);
const files = fs.readdirSync(sourceDir).filter(file => {
if (pattern.includes('*')) {
const regex = new RegExp(pattern.replace('*', '.*'));
return regex.test(file);
}
return file.endsWith(pattern);
});
files.forEach(file => {
const sourcePath = path.join(sourceDir, file);
const destPath = path.join(to, file);
fs.copySync(sourcePath, destPath);
filesMoved++;
logInfo(` ${file}${path.relative(process.cwd(), destPath)}`);
});
}
});
logSuccess(`${filesMoved} archivos organizados correctamente`);
}
// Limpiar directorio temporal
function cleanup(tempDir) {
if (fs.existsSync(tempDir)) {
fs.removeSync(tempDir);
logInfo('Archivos temporales eliminados');
}
}
// Generar reporte
function generateReport(outputDir, analysis) {
logStep('Generando reporte de generación...');
const report = {
timestamp: new Date().toISOString(),
tags: analysis.tags.length,
endpoints: Object.keys(analysis.paths).length,
outputDirectory: outputDir,
structure: {
dtos: fs.readdirSync(path.join(outputDir, 'data/dtos')).length,
repositories: fs.readdirSync(path.join(outputDir, 'data/repositories')).length,
mappers: fs.readdirSync(path.join(outputDir, 'data/mappers')).length,
useCases: fs.readdirSync(path.join(outputDir, 'domain/use-cases')).length
}
};
const reportPath = path.join(process.cwd(), 'generation-report.json');
fs.writeJsonSync(reportPath, report, { spaces: 2 });
logSuccess(`Reporte guardado en: ${reportPath}`);
return report;
}
// Función principal
async function main() {
console.log('\n' + '='.repeat(60));
log(' OpenAPI Clean Architecture Generator', 'bright');
log(' Angular + Clean Architecture Code Generator', 'cyan');
console.log('='.repeat(60) + '\n');
// Validar archivo de entrada
if (!fs.existsSync(options.input)) {
logError(`Archivo no encontrado: ${options.input}`);
process.exit(1);
}
logInfo(`Archivo de entrada: ${options.input}`);
logInfo(`Directorio de salida: ${options.output}`);
logInfo(`Templates: ${options.templates}`);
if (options.dryRun) {
logWarning('Modo DRY RUN - No se generarán archivos');
}
// Verificar/Instalar OpenAPI Generator
if (!checkOpenApiGenerator()) {
logWarning('OpenAPI Generator CLI no encontrado');
if (!options.skipInstall) {
installOpenApiGenerator();
} else {
logError('Instala openapi-generator-cli con: npm install -g @openapitools/openapi-generator-cli');
process.exit(1);
}
} else {
logSuccess('OpenAPI Generator CLI encontrado');
}
// Analizar Swagger
const analysis = analyzeSwagger(options.input);
if (options.dryRun) {
logInfo('Finalizando en modo DRY RUN');
return;
}
// Crear estructura de directorios
createDirectoryStructure(options.output);
// Generar código
const tempDir = generateCode(options.input, options.templates);
// Organizar archivos
organizeFiles(tempDir, options.output);
// Limpiar
cleanup(tempDir);
// Generar reporte
const report = generateReport(options.output, analysis);
// Resumen final
console.log('\n' + '='.repeat(60));
log(' ✨ Generación completada con éxito', 'green');
console.log('='.repeat(60));
console.log(`\n📊 Resumen:`);
console.log(` - DTOs generados: ${report.structure.dtos}`);
console.log(` - Repositories: ${report.structure.repositories}`);
console.log(` - Mappers: ${report.structure.mappers}`);
console.log(` - Use Cases: ${report.structure.useCases}`);
console.log(`\n📁 Archivos generados en: ${colors.cyan}${options.output}${colors.reset}\n`);
}
// Ejecutar
main().catch(error => {
logError(`Error fatal: ${error.message}`);
console.error(error);
process.exit(1);
});

23
openapitools.json Normal file
View File

@@ -0,0 +1,23 @@
{
"$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "7.2.0",
"generators": {
"typescript-angular-clean": {
"generatorName": "typescript-angular",
"output": "./.temp-generated",
"glob": "**/*",
"additionalProperties": {
"ngVersion": "17.0.0",
"modelPropertyNaming": "camelCase",
"supportsES6": true,
"withInterfaces": true,
"providedInRoot": false,
"npmName": "api-client",
"npmVersion": "1.0.0"
}
}
}
}
}

31
package.json Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "openapi-clean-arch-generator",
"version": "1.0.0",
"description": "Generador de código Angular con Clean Architecture desde OpenAPI/Swagger",
"main": "generate.js",
"bin": {
"generate-clean-arch": "./generate.js"
},
"scripts": {
"generate": "node generate.js",
"setup": "npm install -g @openapitools/openapi-generator-cli"
},
"keywords": [
"openapi",
"swagger",
"angular",
"clean-architecture",
"code-generator"
],
"author": "Blas",
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2",
"commander": "^11.1.0",
"fs-extra": "^11.2.0",
"js-yaml": "^4.1.0"
},
"engines": {
"node": ">=14.0.0"
}
}

View File

@@ -0,0 +1,34 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
import { InjectionToken } from '@angular/core';
import { Observable } from 'rxjs';
{{#imports}}
import { {{classname}} } from '@/entities/models/{{classFilename}}.model';
{{/imports}}
/**
* {{classname}} Repository Contract
* Generated from OpenAPI tag: {{classname}}
*/
export interface {{classname}}Repository {
{{#operation}}
/**
* {{summary}}
{{#notes}}
* {{notes}}
{{/notes}}
{{#allParams}}
* @param {{paramName}} {{description}}
{{/allParams}}
*/
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{dataType}}{{^-last}}, {{/-last}}{{/allParams}}): Observable<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}>;
{{/operation}}
}
export const {{constantName}}_REPOSITORY = new InjectionToken<{{classname}}Repository>('{{constantName}}_REPOSITORY');
{{/operations}}
{{/apis}}
{{/apiInfo}}

View File

@@ -0,0 +1,61 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { environment } from '@environment';
import { MRepository } from '@mercadona/core/utils/repository';
import { {{classname}}Repository } from '../../../domain/repositories/{{classFilename}}.repository.contract';
{{#imports}}
import { {{classname}}Dto } from '@/dtos/{{classFilename}}/{{classFilename}}.dto';
import { {{classname}} } from '@/entities/models/{{classFilename}}.model';
import { {{classVarName}}Mapper } from '@/mappers/{{classFilename}}/{{classFilename}}.mapper';
{{/imports}}
/**
* {{classname}} Repository Implementation
* Generated from OpenAPI tag: {{classname}}
*/
@Injectable()
export class {{classname}}RepositoryImpl extends MRepository implements {{classname}}Repository {
constructor() {
super(`${environment.modapApi.url}`);
}
{{#operation}}
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{dataType}}{{^-last}}, {{/-last}}{{/allParams}}): Observable<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}> {
{{#isListContainer}}
return this.{{httpMethod}}<{{returnBaseType}}Dto>('{{path}}'{{#hasQueryParams}}, {
params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }
}{{/hasQueryParams}}{{#hasBodyParam}}, {{bodyParam}}{{/hasBodyParam}})
.pipe(
map((response) => response.{{#vendorExtensions}}{{x-response-property}}{{/vendorExtensions}}{{^vendorExtensions}}items{{/vendorExtensions}}.map({{returnBaseType}}Mapper))
);
{{/isListContainer}}
{{^isListContainer}}
{{#returnType}}
return this.{{httpMethod}}<{{returnType}}Dto>('{{path}}'{{#hasQueryParams}}, {
params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }
}{{/hasQueryParams}}{{#hasBodyParam}}, {{bodyParam}}{{/hasBodyParam}})
.pipe(
map({{returnType}}Mapper)
);
{{/returnType}}
{{^returnType}}
return this.{{httpMethod}}<void>('{{path}}'{{#hasQueryParams}}, {
params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }
}{{/hasQueryParams}}{{#hasBodyParam}}, {{bodyParam}}{{/hasBodyParam}});
{{/returnType}}
{{/isListContainer}}
}
{{/operation}}
}
{{/operations}}
{{/apis}}
{{/apiInfo}}

View File

@@ -0,0 +1,34 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
import { InjectionToken } from '@angular/core';
import { Observable } from 'rxjs';
{{#imports}}
import { {{classname}} } from '@/entities/models/{{classFilename}}.model';
{{/imports}}
/**
* {{classname}} Use Cases Contract
* Generated from OpenAPI tag: {{classname}}
*/
export interface {{classname}}UseCases {
{{#operation}}
/**
* {{summary}}
{{#notes}}
* {{notes}}
{{/notes}}
{{#allParams}}
* @param {{paramName}} {{description}}
{{/allParams}}
*/
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{dataType}}{{^-last}}, {{/-last}}{{/allParams}}): Observable<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}>;
{{/operation}}
}
export const {{constantName}}_USE_CASES = new InjectionToken<{{classname}}UseCases>('{{constantName}}_USE_CASES');
{{/operations}}
{{/apis}}
{{/apiInfo}}

View File

@@ -0,0 +1,32 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
import { inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { {{classname}}UseCases } from './{{classFilename}}.use-cases.contract';
import { {{constantName}}_REPOSITORY, {{classname}}Repository } from '@/domain/repositories/{{classFilename}}.repository.contract';
{{#imports}}
import { {{classname}} } from '@/entities/models/{{classFilename}}.model';
{{/imports}}
/**
* {{classname}} Use Cases Implementation
* Generated from OpenAPI tag: {{classname}}
*/
@Injectable()
export class {{classname}}UseCasesImpl implements {{classname}}UseCases {
#{{classVarName}}Repository: {{classname}}Repository = inject({{constantName}}_REPOSITORY);
{{#operation}}
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{dataType}}{{^-last}}, {{/-last}}{{/allParams}}): Observable<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}> {
return this.#{{classVarName}}Repository.{{nickname}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
}
{{/operation}}
}
{{/operations}}
{{/apis}}
{{/apiInfo}}

28
templates/mapper.mustache Normal file
View File

@@ -0,0 +1,28 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
import { MapFromFn } from '@mercadona/common/public';
import { Builder } from '@mercadona/common/utils';
import { {{classname}}Dto } from '@/dtos/{{classFilename}}/{{classFilename}}.dto';
import { {{classname}} } from '@/entities/models/{{classFilename}}.model';
/**
* {{classname}} Mapper
* Converts DTO to Domain Entity
* Generated from OpenAPI schema: {{classname}}
*/
export const {{classVarName}}Mapper: MapFromFn<{{classname}}Dto, {{classname}}> = (dto: {{classname}}Dto): {{classname}} =>
Builder.forModel({{classname}})
{{#allModels}}
{{#model}}
{{#vars}}
.{{name}}(dto.{{name}})
{{/vars}}
{{/model}}
{{/allModels}}
.build();
{{/operations}}
{{/apis}}
{{/apiInfo}}

View File

@@ -0,0 +1,24 @@
{{#models}}
{{#model}}
{{#imports}}
import { {{classname}} } from './{{classFilename}}.model';
{{/imports}}
/**
* {{classname}} Entity
* {{#description}}{{description}}{{/description}}
* Generated from OpenAPI schema
*/
export class {{classname}} {
{{#vars}}
{{#description}}
/**
* {{description}}
*/
{{/description}}
{{name}}{{^required}}?{{/required}}: {{dataType}};
{{/vars}}
}
{{/model}}
{{/models}}

20
templates/model.mustache Normal file
View File

@@ -0,0 +1,20 @@
{{#models}}
{{#model}}
/**
* {{classname}} DTO
* {{#description}}{{description}}{{/description}}
* Generated from OpenAPI specification
*/
export interface {{classname}}Dto {
{{#vars}}
{{#description}}
/**
* {{description}}
*/
{{/description}}
{{name}}{{^required}}?{{/required}}: {{dataType}};
{{/vars}}
}
{{/model}}
{{/models}}

View File

@@ -0,0 +1,20 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
import { Provider } from '@angular/core';
import { {{constantName}}_REPOSITORY } from '@/domain/repositories/{{classFilename}}.repository.contract';
import { {{classname}}RepositoryImpl } from '@/data/repositories/{{classFilename}}.repository.impl';
/**
* {{classname}} Repository Provider
* Binds the repository contract with its implementation
*/
export const {{classname}}RepositoryProvider: Provider = {
provide: {{constantName}}_REPOSITORY,
useClass: {{classname}}RepositoryImpl
};
{{/operations}}
{{/apis}}
{{/apiInfo}}

View File

@@ -0,0 +1,20 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
import { Provider } from '@angular/core';
import { {{constantName}}_USE_CASES } from '@/domain/use-cases/{{classFilename}}/{{classFilename}}.use-cases.contract';
import { {{classname}}UseCasesImpl } from '@/domain/use-cases/{{classFilename}}/{{classFilename}}.use-cases.impl';
/**
* {{classname}} Use Cases Provider
* Binds the use cases contract with its implementation
*/
export const {{classname}}UseCasesProvider: Provider = {
provide: {{constantName}}_USE_CASES,
useClass: {{classname}}UseCasesImpl
};
{{/operations}}
{{/apis}}
{{/apiInfo}}