Model generator
This commit is contained in:
13
CLAUDE.md
13
CLAUDE.md
@@ -23,6 +23,19 @@
|
||||
- **Model**: Shared domain models
|
||||
- **Tools**: `peakup-maven` – Maven plugin with two mojos: `PackageInfoGeneratorMojo` (generates `package-info.java`
|
||||
with `@NonNull` per package) and `TypeScriptClientGeneratorMojo` (generates TS client from REST endpoints)
|
||||
- **Tools**: `peakup-tools` – standalone dev utilities:
|
||||
- `ModelGeneratorTool` – generates a full domain slice (entity, DAO, service, resource) from a JSON definition file.
|
||||
Schema: `peakup-tools/src/main/resources/model-definition-schema.json`
|
||||
Example: `peakup-tools/src/main/resources/example-model-definition.json`
|
||||
Run (from project root):
|
||||
```
|
||||
$MVN -f peakup-tools/pom.xml -Dmaven.repo.local=/home/ingo/.m2/peak-repo \
|
||||
compile exec:java -Dexec.mainClass=com.peakup.tools.generator.ModelGeneratorTool \
|
||||
"-Dexec.args=path/to/definition.json"
|
||||
```
|
||||
Output defaults to `peakup-backend/src/main/java/`. Pass a second arg to override.
|
||||
Generated service only contains `getDAO()` + domain-specific methods — CRUD is inherited from
|
||||
`AbstractService` as `public final @Transactional`.
|
||||
|
||||
**Backend Architecture**:
|
||||
|
||||
|
||||
215
peakup-tools/src/main/resources/model-definition-schema.json
Normal file
215
peakup-tools/src/main/resources/model-definition-schema.json
Normal file
@@ -0,0 +1,215 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "model-definition-schema.json",
|
||||
"title": "ModelDefinition",
|
||||
"description": "Input schema for ModelGeneratorTool. Describes a single JPA entity and generates model, DAO, service and resource.",
|
||||
"type": "object",
|
||||
"required": ["modelName", "packageBase", "tableName", "baseClass", "apiPath"],
|
||||
"additionalProperties": true,
|
||||
"properties": {
|
||||
"modelName": {
|
||||
"type": "string",
|
||||
"description": "PascalCase class name, e.g. 'TrainingNote'."
|
||||
},
|
||||
"packageBase": {
|
||||
"type": "string",
|
||||
"description": "Root package for all generated classes, e.g. 'com.peakup.trainingnote'.",
|
||||
"pattern": "^[a-z][a-z0-9]*(\\.[a-z][a-z0-9]*)*$"
|
||||
},
|
||||
"tableName": {
|
||||
"type": "string",
|
||||
"description": "Database table name, e.g. 'training_note'."
|
||||
},
|
||||
"baseClass": {
|
||||
"type": "string",
|
||||
"description": "Superclass of the generated entity.",
|
||||
"enum": ["AbstractEntity", "AbstractTimestampedEntity"]
|
||||
},
|
||||
"apiPath": {
|
||||
"type": "string",
|
||||
"description": "JAX-RS @Path value on the resource, e.g. '/api/training-notes'.",
|
||||
"pattern": "^/"
|
||||
},
|
||||
"jsonIncludeNonNull": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "When true, adds @JsonInclude(NON_NULL) to the entity class."
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"description": "Scalar and enum fields mapped to @Column.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name", "javaType", "nullable", "columnName"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "camelCase field name, e.g. 'athleteId'."
|
||||
},
|
||||
"javaType": {
|
||||
"type": "string",
|
||||
"description": "Java type. Primitives: int, long, double, float, boolean. Wrappers: Integer, Long, Double, Float, Boolean. String, Instant, LocalDate. Arrays: int[], long[], double[], String[] (use jdbcTypeCode=ARRAY). Any other simple name is resolved to packageBase.model, or provide a fully-qualified name."
|
||||
},
|
||||
"nullable": {
|
||||
"type": "boolean",
|
||||
"description": "When true, adds @Nullable and omits nullable=false on @Column."
|
||||
},
|
||||
"columnName": {
|
||||
"type": "string",
|
||||
"description": "Database column name, snake_case, e.g. 'athlete_id'."
|
||||
},
|
||||
"columnDefinition": {
|
||||
"type": "string",
|
||||
"description": "Optional JPA columnDefinition override, e.g. 'TEXT', 'integer[]', 'text[]'."
|
||||
},
|
||||
"jdbcTypeCode": {
|
||||
"type": "string",
|
||||
"description": "Optional Hibernate SqlTypes constant name. Currently only 'ARRAY' is supported.",
|
||||
"enum": ["ARRAY"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"elementCollections": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"description": "Fields mapped via @ElementCollection(fetch=LAZY). Supports both scalar (String) and embeddable collections.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["fieldName", "collectionTable", "joinColumn", "valueType"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"fieldName": {
|
||||
"type": "string",
|
||||
"description": "camelCase field name on the entity, e.g. 'tags'."
|
||||
},
|
||||
"collectionTable": {
|
||||
"type": "string",
|
||||
"description": "Join table name, e.g. 'training_note_tag'."
|
||||
},
|
||||
"joinColumn": {
|
||||
"type": "string",
|
||||
"description": "FK column in the join table pointing to the owning entity, e.g. 'training_note_id'."
|
||||
},
|
||||
"valueType": {
|
||||
"type": "string",
|
||||
"description": "Element type. Use 'String' for scalar collections; use a class name for @Embeddable collections. Simple names are resolved to packageBase.model."
|
||||
},
|
||||
"valueColumn": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Column name for scalar (String) collections, e.g. 'tag'. Omit or set null for embeddable collections."
|
||||
},
|
||||
"orderColumn": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Column name for @OrderColumn, e.g. 'sort_index'. Null omits the annotation."
|
||||
},
|
||||
"embeddable": {
|
||||
"description": "Inline @Embeddable class definition. When present, a separate embeddable source file is generated.",
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["className", "fields"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"className": {
|
||||
"type": "string",
|
||||
"description": "PascalCase class name for the embeddable, e.g. 'NoteAttachment'."
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"description": "Fields of the embeddable. Same structure as top-level fields.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name", "javaType", "nullable", "columnName"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"javaType": { "type": "string" },
|
||||
"nullable": { "type": "boolean" },
|
||||
"columnName": { "type": "string" },
|
||||
"columnDefinition": { "type": "string" },
|
||||
"jdbcTypeCode": { "type": "string", "enum": ["ARRAY"] }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"oneToManyRelations": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"description": "Fields mapped via @OneToMany(fetch=EAGER). Target entity must exist independently.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["fieldName", "entityClass", "joinColumn", "orderBy"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"fieldName": {
|
||||
"type": "string",
|
||||
"description": "camelCase field name on the entity, e.g. 'steps'."
|
||||
},
|
||||
"entityClass": {
|
||||
"type": "string",
|
||||
"description": "Simple or fully-qualified target entity class name. Simple names are resolved to packageBase.model; FQNs (containing a dot) are used as-is."
|
||||
},
|
||||
"joinColumn": {
|
||||
"type": "string",
|
||||
"description": "FK column on the target entity side, e.g. 'training_note_id'."
|
||||
},
|
||||
"orderBy": {
|
||||
"type": "string",
|
||||
"description": "JPQL @OrderBy clause, e.g. 'sortIndex ASC'."
|
||||
},
|
||||
"fetchMode": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Hibernate @Fetch mode constant, e.g. 'SUBSELECT'. Null omits the annotation.",
|
||||
"enum": ["SUBSELECT", "SELECT", "JOIN", null]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"enums": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"description": "Enums to generate alongside the entity. Each implements PersistableEnum and includes an inner Converter with autoApply=true.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["className", "values"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"className": {
|
||||
"type": "string",
|
||||
"description": "PascalCase enum name, e.g. 'NoteTypeEnum'."
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"description": "Ordered list of enum constants.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name", "key"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "UPPER_SNAKE_CASE constant name, e.g. 'GENERAL'."
|
||||
},
|
||||
"key": {
|
||||
"type": "integer",
|
||||
"description": "Stable integer persisted to the database. Must be unique within the enum."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user