UNPKG

claude-buddy

Version:

Claude Buddy installation and setup script

534 lines (444 loc) 15.3 kB
--- name: jhipster-development description: JHipster full-stack application development including Angular, Angular Material, and JHipster-specific patterns. Use when working with JHipster projects, Angular applications, or full-stack Java/Spring Boot + Angular development. Auto-activates for JHipster project files and Angular code. --- # JHipster Development Skill Comprehensive guidance for JHipster application development, including Angular frontend, Spring Boot backend, and JHipster-specific conventions and best practices. ## When to Use This Skill - Developing JHipster applications (monolith or microservices) - Building Angular frontends with JHipster - Working with Angular Material in JHipster projects - Entity generation and database schema management - Implementing authentication and authorization - Microservices architecture with JHipster - API gateway configuration - Service discovery and registry integration ## Auto-Activation This skill automatically activates when: - Project contains .yo-rc.json (JHipster configuration) - Keywords: JHipster, jhipster, Angular, Spring Boot, entity, gateway - Working with JHipster-generated files - Angular project with JHipster structure - Discussion of full-stack development patterns ## Reference Materials This skill includes comprehensive documentation: - **[jhipster.md](jhipster.md)**: Core JHipster concepts and patterns - **[angular-js.md](angular-js.md)**: Angular framework integration - **[angular-material.md](angular-material.md)**: Angular Material UI components ## Key Concepts ### JHipster Application Structure JHipster generates a complete full-stack application with: **Frontend (Angular)**: - `/src/main/webapp/app/` - Angular application code - Component-based architecture - Lazy-loaded modules - Reactive forms - NgRx for state management (optional) **Backend (Spring Boot)**: - `/src/main/java/` - Java backend code - Spring Boot REST APIs - Spring Security for authentication - JPA/Hibernate for data access - Liquibase for database migrations **Configuration**: - `.yo-rc.json` - JHipster project configuration - `/src/main/resources/config/` - Application properties - Docker Compose configurations ### Entity Management **Generate Entity**: ```bash jhipster entity Customer ``` **JDL (JHipster Domain Language)**: ```jdl entity Customer { firstName String required lastName String required email String pattern(/^[^@\s]+@[^@\s]+\.[^@\s]+$/) phoneNumber String } entity Order { orderDate Instant required status OrderStatus required totalAmount BigDecimal required min(0) } enum OrderStatus { PENDING, COMPLETED, CANCELLED } relationship OneToMany { Customer{orders} to Order{customer(email) required} } paginate Order with pagination service all with serviceImpl ``` **Import JDL**: ```bash jhipster jdl my-model.jdl ``` ### Angular Component Development **JHipster-Generated Component**: ```typescript import { Component, OnInit } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; import { Customer, ICustomer } from '../customer.model'; import { CustomerService } from '../service/customer.service'; @Component({ selector: 'jhi-customer', templateUrl: './customer.component.html', }) export class CustomerComponent implements OnInit { customers?: ICustomer[]; isLoading = false; constructor(protected customerService: CustomerService) {} ngOnInit(): void { this.loadAll(); } loadAll(): void { this.isLoading = true; this.customerService.query().subscribe({ next: (res: HttpResponse<ICustomer[]>) => { this.isLoading = false; this.customers = res.body ?? []; }, error: () => { this.isLoading = false; }, }); } } ``` **Component Template**: ```html <div> <h2 id="page-heading"> <span>Customers</span> <div class="d-flex justify-content-end"> <button class="btn btn-primary" [routerLink]="['/customer/new']"> <fa-icon icon="plus"></fa-icon> <span>Create new Customer</span> </button> </div> </h2> <jhi-alert-error></jhi-alert-error> <div class="alert alert-warning" *ngIf="customers && customers.length === 0"> No customers found </div> <div class="table-responsive" *ngIf="customers && customers.length > 0"> <table class="table table-striped"> <thead> <tr> <th><span>ID</span></th> <th><span>First Name</span></th> <th><span>Last Name</span></th> <th><span>Email</span></th> <th></th> </tr> </thead> <tbody> <tr *ngFor="let customer of customers; trackBy: trackId"> <td>{{ customer.id }}</td> <td>{{ customer.firstName }}</td> <td>{{ customer.lastName }}</td> <td>{{ customer.email }}</td> <td class="text-end"> <div class="btn-group"> <button type="submit" [routerLink]="['/customer', customer.id, 'view']" class="btn btn-info btn-sm"> <fa-icon icon="eye"></fa-icon> <span class="d-none d-md-inline">View</span> </button> <button type="submit" [routerLink]="['/customer', customer.id, 'edit']" class="btn btn-primary btn-sm"> <fa-icon icon="pencil-alt"></fa-icon> <span class="d-none d-md-inline">Edit</span> </button> <button type="submit" (click)="delete(customer)" class="btn btn-danger btn-sm"> <fa-icon icon="times"></fa-icon> <span class="d-none d-md-inline">Delete</span> </button> </div> </td> </tr> </tbody> </table> </div> </div> ``` ### Angular Material Integration **Import Material Modules**: ```typescript import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; @NgModule({ imports: [ MatButtonModule, MatCardModule, MatFormFieldModule, MatInputModule, ], }) export class SharedModule {} ``` **Material Form**: ```html <mat-card> <mat-card-header> <mat-card-title>Customer Form</mat-card-title> </mat-card-header> <mat-card-content> <form [formGroup]="customerForm" (ngSubmit)="save()"> <mat-form-field appearance="fill"> <mat-label>First Name</mat-label> <input matInput formControlName="firstName" required> <mat-error *ngIf="customerForm.get('firstName')?.hasError('required')"> First name is required </mat-error> </mat-form-field> <mat-form-field appearance="fill"> <mat-label>Email</mat-label> <input matInput type="email" formControlName="email" required> <mat-error *ngIf="customerForm.get('email')?.hasError('email')"> Invalid email format </mat-error> </mat-form-field> </form> </mat-card-content> <mat-card-actions> <button mat-raised-button color="primary" (click)="save()">Save</button> <button mat-button (click)="cancel()">Cancel</button> </mat-card-actions> </mat-card> ``` ### Backend API Development **REST Controller**: ```java @RestController @RequestMapping("/api") public class CustomerResource { private final CustomerService customerService; public CustomerResource(CustomerService customerService) { this.customerService = customerService; } @PostMapping("/customers") public ResponseEntity<CustomerDTO> createCustomer(@Valid @RequestBody CustomerDTO customerDTO) { CustomerDTO result = customerService.save(customerDTO); return ResponseEntity.created(new URI("/api/customers/" + result.getId())) .headers(HeaderUtil.createEntityCreationAlert("customer", result.getId().toString())) .body(result); } @GetMapping("/customers") public ResponseEntity<List<CustomerDTO>> getAllCustomers(Pageable pageable) { Page<CustomerDTO> page = customerService.findAll(pageable); HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/customers"); return ResponseEntity.ok().headers(headers).body(page.getContent()); } @GetMapping("/customers/{id}") public ResponseEntity<CustomerDTO> getCustomer(@PathVariable Long id) { Optional<CustomerDTO> customerDTO = customerService.findOne(id); return ResponseUtil.wrapOrNotFound(customerDTO); } @DeleteMapping("/customers/{id}") public ResponseEntity<Void> deleteCustomer(@PathVariable Long id) { customerService.delete(id); return ResponseEntity.noContent() .headers(HeaderUtil.createEntityDeletionAlert("customer", id.toString())) .build(); } } ``` **Service Layer**: ```java @Service @Transactional public class CustomerService { private final CustomerRepository customerRepository; private final CustomerMapper customerMapper; public CustomerService(CustomerRepository customerRepository, CustomerMapper customerMapper) { this.customerRepository = customerRepository; this.customerMapper = customerMapper; } public CustomerDTO save(CustomerDTO customerDTO) { Customer customer = customerMapper.toEntity(customerDTO); customer = customerRepository.save(customer); return customerMapper.toDto(customer); } @Transactional(readOnly = true) public Page<CustomerDTO> findAll(Pageable pageable) { return customerRepository.findAll(pageable) .map(customerMapper::toDto); } @Transactional(readOnly = true) public Optional<CustomerDTO> findOne(Long id) { return customerRepository.findById(id) .map(customerMapper::toDto); } public void delete(Long id) { customerRepository.deleteById(id); } } ``` ### Authentication & Security **JWT Authentication**: - JHipster uses JWT tokens for stateless authentication - Tokens stored in localStorage or sessionStorage - Automatic token refresh **Security Configuration**: ```java @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/api/authenticate").permitAll() .antMatchers("/api/**").authenticated() .and() .apply(securityConfigurerAdapter()); } } ``` **Role-Based Access**: ```typescript // In Angular <button *jhiHasAnyAuthority="'ROLE_ADMIN'" [routerLink]="['/admin']"> Admin Panel </button> ``` ```java // In Spring Boot @PreAuthorize("hasAuthority('ROLE_ADMIN')") @GetMapping("/admin/users") public List<UserDTO> getAllUsers() { return userService.findAll(); } ``` ### Microservices Architecture **JHipster Registry** (Service Discovery): ```yaml # application-dev.yml eureka: client: service-url: defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/ ``` **API Gateway**: ```yaml # application.yml (gateway) zuul: routes: customer-service: path: /services/customer/** serviceId: customer order-service: path: /services/order/** serviceId: order ``` ### Database Management **Liquibase Changelog**: ```xml <changeSet id="20231001000001" author="jhipster"> <createTable tableName="customer"> <column name="id" type="bigint" autoIncrement="true"> <constraints primaryKey="true" nullable="false"/> </column> <column name="first_name" type="varchar(50)"> <constraints nullable="false"/> </column> <column name="last_name" type="varchar(50)"> <constraints nullable="false"/> </column> <column name="email" type="varchar(100)"> <constraints nullable="false" unique="true"/> </column> </createTable> </changeSet> ``` ## Common Commands ```bash # Generate new entity jhipster entity Product # Import JDL file jhipster jdl my-model.jdl # Run development server ./mvnw # Backend npm start # Frontend # Run tests ./mvnw test # Backend tests npm test # Frontend tests # Build for production ./mvnw -Pprod package npm run build # Generate Docker configuration jhipster docker-compose # Upgrade JHipster jhipster upgrade ``` ## Best Practices 1. **Use JDL for Entity Modeling**: Maintain entities in JDL files for consistency 2. **Follow Generated Patterns**: Stick to JHipster conventions for maintainability 3. **Customize Carefully**: Override templates only when necessary 4. **Test Thoroughly**: Use generated test scaffolding 5. **Version Control**: Commit `.yo-rc.json` and JDL files 6. **Environment Configuration**: Use Spring profiles for different environments 7. **API Versioning**: Plan for API evolution 8. **Performance**: Use pagination and lazy loading 9. **Security**: Review and customize security configuration 10. **Documentation**: Use Swagger/OpenAPI for API documentation ## Testing ### Angular Testing ```typescript describe('CustomerComponent', () => { let component: CustomerComponent; let fixture: ComponentFixture<CustomerComponent>; let service: CustomerService; beforeEach(() => { TestBed.configureTestingModule({ declarations: [CustomerComponent], imports: [HttpClientTestingModule], providers: [CustomerService], }); fixture = TestBed.createComponent(CustomerComponent); component = fixture.componentInstance; service = TestBed.inject(CustomerService); }); it('should load customers on init', () => { const customers = [{ id: 1, firstName: 'John', lastName: 'Doe' }]; jest.spyOn(service, 'query').mockReturnValue(of(new HttpResponse({ body: customers }))); component.ngOnInit(); expect(component.customers).toEqual(customers); }); }); ``` ### Spring Boot Testing ```java @SpringBootTest(classes = MyApp.class) @AutoConfigureMockMvc @WithMockUser public class CustomerResourceIT { @Autowired private MockMvc restCustomerMockMvc; @Autowired private CustomerRepository customerRepository; @Test @Transactional public void createCustomer() throws Exception { CustomerDTO customerDTO = new CustomerDTO(); customerDTO.setFirstName("John"); customerDTO.setLastName("Doe"); customerDTO.setEmail("john@example.com"); restCustomerMockMvc.perform(post("/api/customers") .contentType(MediaType.APPLICATION_JSON) .content(TestUtil.convertObjectToJsonBytes(customerDTO))) .andExpect(status().isCreated()); List<Customer> customerList = customerRepository.findAll(); assertThat(customerList).hasSize(1); } } ``` ## Resources For detailed examples and comprehensive documentation, see: - [jhipster.md](jhipster.md) - Core JHipster patterns - [angular-js.md](angular-js.md) - Angular integration - [angular-material.md](angular-material.md) - Material UI components