added authentication
This commit is contained in:
parent
13a61aea03
commit
a105d2c9e9
27
pom.xml
27
pom.xml
|
|
@ -36,19 +36,32 @@
|
|||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<!-- <dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency> -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-boot-starter</artifactId>
|
||||
<version>24.0.1</version>
|
||||
</dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||
</dependency> -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId> <!-- oppure jjwt-gson -->
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- PostgreSQL database -->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
|
|
@ -89,7 +102,7 @@
|
|||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
package it.cnr.isti.workflow.manager;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import it.cnr.isti.workflow.manager.model.entities.LoginEntity;
|
||||
|
||||
@Component
|
||||
public class JwtUtil {
|
||||
|
||||
SecretKey secretKey = Keys.hmacShaKeyFor("la-tua-chiave-segreta-di-almeno-32-byte!".getBytes());
|
||||
|
||||
public String generateToken(String username) {
|
||||
String jwt = Jwts.builder()
|
||||
.setSubject(username)
|
||||
.setIssuedAt(new Date())
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 1 giorno
|
||||
.signWith(secretKey, SignatureAlgorithm.HS256) // Usa una SecretKey (HMAC)
|
||||
.compact();
|
||||
return jwt;
|
||||
}
|
||||
|
||||
public String extractUsername(String token) {
|
||||
return Jwts.parserBuilder()
|
||||
.setSigningKey(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody()
|
||||
.getSubject();
|
||||
}
|
||||
|
||||
public boolean validateToken(String token, LoginEntity userDetails) {
|
||||
final String username = extractUsername(token);
|
||||
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
||||
}
|
||||
|
||||
private boolean isTokenExpired(String token) {
|
||||
Date expiration = Jwts.parserBuilder()
|
||||
.setSigningKey(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody()
|
||||
.getExpiration();
|
||||
return expiration.before(new Date());
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,6 @@ import it.cnr.isti.workflow.manager.model.ExecutionStep;
|
|||
import it.cnr.isti.workflow.manager.model.types.NodeDefinition;
|
||||
import it.cnr.isti.workflow.manager.model.ui.Connection;
|
||||
import it.cnr.isti.workflow.manager.model.ui.Flow;
|
||||
import it.cnr.isti.workflow.manager.model.ui.IOModel;
|
||||
import it.cnr.isti.workflow.manager.model.ui.Node;
|
||||
import it.cnr.isti.workflow.manager.repositories.NodeDefinitionRepository;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import org.springframework.util.ResourceUtils;
|
|||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import it.cnr.isti.workflow.manager.executors.ai.AIModel;
|
||||
import it.cnr.isti.workflow.manager.model.entities.LoginEntity;
|
||||
import it.cnr.isti.workflow.manager.model.types.IOType;
|
||||
import it.cnr.isti.workflow.manager.model.types.Key;
|
||||
import it.cnr.isti.workflow.manager.model.types.NodeDefinition;
|
||||
|
|
@ -21,6 +22,7 @@ import it.cnr.isti.workflow.manager.model.types.ParameterType;
|
|||
import it.cnr.isti.workflow.manager.model.types.Translator;
|
||||
import it.cnr.isti.workflow.manager.model.ui.Flow;
|
||||
import it.cnr.isti.workflow.manager.model.ui.FlowEntity;
|
||||
import it.cnr.isti.workflow.manager.repositories.AuthRepository;
|
||||
import it.cnr.isti.workflow.manager.repositories.FlowRepository;
|
||||
import it.cnr.isti.workflow.manager.repositories.NodeDefinitionRepository;
|
||||
|
||||
|
|
@ -36,7 +38,7 @@ public class WorkflowManagerApplication {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "app", name = "db.init.enabled", havingValue = "true")
|
||||
CommandLineRunner init(NodeDefinitionRepository repository, FlowRepository flowRepository) {
|
||||
CommandLineRunner init(NodeDefinitionRepository repository, FlowRepository flowRepository, AuthRepository authRepository) {
|
||||
return args -> {
|
||||
|
||||
JSONObject rPars = new JSONObject().put("options",
|
||||
|
|
@ -69,6 +71,8 @@ public class WorkflowManagerApplication {
|
|||
loadNodeDefinitionAndSave(repository);
|
||||
|
||||
loadFlowsAndSave(flowRepository);
|
||||
|
||||
insertUser(authRepository);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -101,4 +105,9 @@ public class WorkflowManagerApplication {
|
|||
}
|
||||
}
|
||||
|
||||
private void insertUser(AuthRepository repository) {
|
||||
LoginEntity le = new LoginEntity("lucio", "lucio");
|
||||
repository.save(le);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
package it.cnr.isti.workflow.manager.configurations;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import it.cnr.isti.workflow.manager.JwtUtil;
|
||||
import it.cnr.isti.workflow.manager.model.entities.LoginEntity;
|
||||
import it.cnr.isti.workflow.manager.repositories.AuthRepository;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
@Component
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Autowired
|
||||
private AuthRepository authRepository;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(@NonNull HttpServletRequest request,
|
||||
@NonNull HttpServletResponse response,
|
||||
@NonNull FilterChain filterChain) throws ServletException, IOException {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
|
||||
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
||||
String jwt = authHeader.substring(7);
|
||||
String username = jwtUtil.extractUsername(jwt);
|
||||
|
||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
LoginEntity userDetails = authRepository.findById(username).orElse(null);
|
||||
|
||||
if (jwtUtil.validateToken(jwt, userDetails)) {
|
||||
UsernamePasswordAuthenticationToken authToken =
|
||||
new UsernamePasswordAuthenticationToken(userDetails, null, Collections.emptyList());
|
||||
|
||||
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,40 @@
|
|||
package it.cnr.isti.workflow.manager.configurations;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
//import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
//import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
/*
|
||||
|
||||
@Autowired
|
||||
private JwtAuthenticationFilter jwtFilter;
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeHttpRequests(authz -> authz
|
||||
.requestMatchers("/public/**").permitAll()
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
return http
|
||||
.cors(Customizer.withDefaults())
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/auth/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
|
||||
|
||||
return http.build();
|
||||
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.build();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
|
||||
return config.getAuthenticationManager();
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ public class WebConfig implements WebMvcConfigurer {
|
|||
registry.addMapping("/**")
|
||||
.allowedOrigins(origins)
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true);
|
||||
.allowedHeaders("*");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
package it.cnr.isti.workflow.manager.controllers;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import it.cnr.isti.workflow.manager.JwtUtil;
|
||||
import it.cnr.isti.workflow.manager.model.AuthRequest;
|
||||
import it.cnr.isti.workflow.manager.model.ChangePasswordRequest;
|
||||
import it.cnr.isti.workflow.manager.services.AuthService;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
public class AuthController {
|
||||
|
||||
private static final Logger logger = org.slf4j.LoggerFactory.getLogger(AuthController.class);
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Autowired
|
||||
private AuthService authService;
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<?> login(@RequestBody AuthRequest request) {
|
||||
logger.info("Login attempt for user: {}", request.getUsername());
|
||||
if (!authService.validateUser(request.getUsername(), request.getPassword())) {
|
||||
return ResponseEntity.status(401).body("Invalid credentials");
|
||||
}
|
||||
String jwt = jwtUtil.generateToken(request.getUsername());
|
||||
return ResponseEntity.ok(Collections.singletonMap("token", jwt));
|
||||
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<?> register(@RequestBody AuthRequest request) {
|
||||
if (authService.registerUser(request.getUsername(), request.getPassword())) {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
return ResponseEntity.badRequest().body("the user already exists");
|
||||
}
|
||||
|
||||
@PostMapping("/change-password")
|
||||
public ResponseEntity<?> changePassword(@RequestBody ChangePasswordRequest request) {
|
||||
if (authService.changePassword(request.getUsername(), request.getOldPassword(), request.getNewPassword())) {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
return ResponseEntity.badRequest().body("Change password failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +46,7 @@ public class GeminiModel implements AIModel {
|
|||
|
||||
// Deserializzazione della risposta JSON in un oggetto GeminiResponse
|
||||
GeminiResponse response = objectMapper.readValue(result, GeminiResponse.class);
|
||||
|
||||
|
||||
// Estrazione del testo dal primo candidato
|
||||
String candidateText = response.getCandidates().get(0).getContent().getParts().get(0).getText();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package it.cnr.isti.workflow.manager.model;
|
||||
|
||||
|
||||
public class AuthRequest {
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
// Getters e Setters
|
||||
public String getUsername() { return username; }
|
||||
public void setUsername(String username) { this.username = username; }
|
||||
|
||||
public String getPassword() { return password; }
|
||||
public void setPassword(String password) { this.password = password; }
|
||||
|
||||
boolean isValid() {
|
||||
return username.contains(" ") && username != null && !username.isEmpty() && username.length()>5 &&
|
||||
password != null && username.contains(" ") && password.length()>5 && !password.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package it.cnr.isti.workflow.manager.model;
|
||||
|
||||
|
||||
public class ChangePasswordRequest {
|
||||
private String username;
|
||||
private String oldPassword;
|
||||
private String newPassword;
|
||||
|
||||
// Getters e Setters
|
||||
public String getUsername() { return username; }
|
||||
public void setUsername(String username) { this.username = username; }
|
||||
|
||||
public String getOldPassword() { return oldPassword; }
|
||||
public void setOldPassword(String oldPassword) { this.oldPassword = oldPassword; }
|
||||
|
||||
public String getNewPassword() { return newPassword; }
|
||||
public void setNewPassword(String newPassword) { this.newPassword = newPassword; }
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
|
|
@ -78,16 +79,25 @@ public class ExecutionStep implements InputReadyListener {
|
|||
if (areAllInputsReady())
|
||||
new Thread(() -> {
|
||||
this.context.getStepsUnderExecution().add(this.id);
|
||||
log.info("starting executing of step {}", this.nodeDefinition.getType());
|
||||
try {
|
||||
Map<String, Object> realInputs = prepareInputs();
|
||||
//log.debug("input translation for step {} is {}", id, realInputs);
|
||||
Map<String, Object> returned = executor.execute(this.runtimeParameters, null, realInputs);
|
||||
this.context.getNodeResult().put(this.id, returned);
|
||||
for (InputReadyListener nextStep : nextSteps)
|
||||
for (Map.Entry<String, Object> entry : returned.entrySet())
|
||||
nextStep.inputReady(!nodeDefinition.getExecutorToOutputTranslationMappings().isEmpty()
|
||||
? nodeDefinition.getExecutorToOutputTranslationMappings().get(entry.getKey())
|
||||
: entry.getKey(), entry.getValue());
|
||||
|
||||
//mapping executor output to current node output
|
||||
Map<String, Object> mappedReturn = returned.entrySet().stream().collect(Collectors.toMap(
|
||||
e -> { return !nodeDefinition.getExecutorToOutputTranslationMappings().isEmpty()
|
||||
? nodeDefinition.getExecutorToOutputTranslationMappings().get(e.getKey())
|
||||
: e.getKey();
|
||||
},
|
||||
e -> e.getValue()));
|
||||
|
||||
context.nodeResult.put(this.id, mappedReturn);
|
||||
|
||||
//sendig output to next steps
|
||||
nextSteps.forEach(n -> mappedReturn.forEach((k, v) -> n.inputReady(k,v)));
|
||||
|
||||
} catch (ExecutionException e) {
|
||||
log.error("error during execution of step {}: {}", id, e.getMessage());
|
||||
this.context.getErrors().add(String.format("[%S] %s", this.id, e.getMessage()));
|
||||
|
|
@ -95,7 +105,9 @@ public class ExecutionStep implements InputReadyListener {
|
|||
return;
|
||||
} finally {
|
||||
this.context.getStepsUnderExecution().remove(this.id);
|
||||
}
|
||||
log.info("finished executing of step {}", this.nodeDefinition.getType());
|
||||
}
|
||||
|
||||
}).start();
|
||||
else
|
||||
log.debug("not all inputs are ready for step {}", id);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package it.cnr.isti.workflow.manager.model.entities;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class LoginEntity {
|
||||
@Id
|
||||
private String username;
|
||||
private String password;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package it.cnr.isti.workflow.manager.repositories;
|
||||
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import it.cnr.isti.workflow.manager.model.entities.LoginEntity;
|
||||
|
||||
@Repository
|
||||
public interface AuthRepository extends JpaRepository<LoginEntity, String> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package it.cnr.isti.workflow.manager.services;
|
||||
|
||||
|
||||
import org.keycloak.adapters.jaas.AbstractKeycloakLoginModule.Auth;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import it.cnr.isti.workflow.manager.model.entities.LoginEntity;
|
||||
import it.cnr.isti.workflow.manager.repositories.AuthRepository;
|
||||
|
||||
|
||||
@Service
|
||||
public class AuthService {
|
||||
|
||||
private static final Logger logger = org.slf4j.LoggerFactory.getLogger(AuthService.class);
|
||||
|
||||
@Autowired
|
||||
AuthRepository authRepository;
|
||||
|
||||
public boolean validateUser(String username, String password) {
|
||||
LoginEntity le = authRepository.findById(username).orElse(null);
|
||||
if (le == null) {
|
||||
return false; // User not found
|
||||
}
|
||||
return le.getPassword().equals(password); // Validate password
|
||||
}
|
||||
|
||||
public boolean registerUser(String username, String password) {
|
||||
if (authRepository.existsById(username)) {
|
||||
return false; // User already exists
|
||||
}
|
||||
authRepository.save(new LoginEntity(username, password));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean changePassword(String username, String oldPassword, String newPassword) {
|
||||
return authRepository.findById(username)
|
||||
.filter(user -> user.getPassword().equals(oldPassword))
|
||||
.map(user -> {
|
||||
user.setPassword(newPassword);
|
||||
authRepository.save(user);
|
||||
return true;
|
||||
})
|
||||
.orElse(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -256,7 +256,7 @@
|
|||
"inputToExecutorTranslationMappings": { },
|
||||
"inputTranslators": {
|
||||
"prompt": {
|
||||
"translation": "Act as a **Senior Software Engineer** specializing in **model-driven development**. Generate clean, maintainable code that:\n1. Implements all validated requirements (from CSV) \n2. Matches the structure of the provided class diagram \n3. Follows the interaction flows from the sequence diagram \n\n**Input:** \n1. requirements (ID, Corrected Requirement, Customer Quote):\n\n${{corrected-requirements}}\n\n2. class diagram (PlantUML) :\n\n${{class-diagram}}\n\n3. sequence diagram (PlantUML) :\n\n\n\n\n**Output Rules:** \n- **Language**: Python\n- **Architecture**: \n - 1:1 mapping of PlantUML classes to code \n - Sequence diagram steps → method logic \n- **Quality**: \n - Type hints (Python)\n - Docstrings referencing requirement IDs (e.g., `# F1`) \n - Appropriate error handling \n- **Format**:\n - Output only executable code, do not add any text, besides comments\n\n**Example Output (Python Calculator):** \n\nfrom enum import Enum, auto\n\nclass OperationType(Enum):\n \"\"\"Supported operations (matches class diagram <<enum>>)\"\"\"\n ADD = auto() # F3: Basic arithmetic\n SUBTRACT = auto() # F3\n MULTIPLY = auto() # F3\n DIVIDE = auto() # F3\n\nclass Calculator:\n \"\"\"Core calculator logic (from class diagram)\"\"\"\n def __init__(self):\n self.current_input = 0 # F1: Digit entry\n self.stored_value = None # For multi-step operations\n self.selected_op = None # F3: Operation selection\n \n def calculate(self) -> float:\n \"\"\"Execute the current operation (sequence diagram '=' press)\"\"\"\n if not self.selected_op:\n return self.current_input\n \n # Sequence diagram compute step\n match self.selected_op:\n case OperationType.ADD:\n result = self.stored_value + self.current_input\n case OperationType.SUBTRACT:\n result = self.stored_value - self.current_input\n # ... other operations (F3)\n \n self.current_input = result\n return result\n\nclass InputHandler:\n \"\"\"Manages user input flow (sequence diagram interactions)\"\"\"\n def __init__(self, calculator: Calculator, display: Display):\n self.calc = calculator\n self.display = display\n \n def handle_digit(self, digit: int): # F1\n \"\"\"Process digit press (sequence diagram step 1)\"\"\"\n self.calc.current_input = self.calc.current_input * 10 + digit\n self.display.update(str(self.calc.current_input))\n\n def handle_operator(self, op: OperationType): # F3\n \"\"\"Process +,-,*,/ press (sequence diagram step 2)\"\"\"\n self.calc.stored_value = self.calc.current_input\n self.calc.selected_op = op\n self.calc.current_input = 0\n"
|
||||
"translation": "Act as a **Senior Software Engineer** specializing in **model-driven development**. Generate clean, maintainable code that:\n1. Implements all validated requirements (from CSV) \n2. Matches the structure of the provided class diagram \n3. Follows the interaction flows from the sequence diagram \n\n**Input:** \n1. requirements (ID, Corrected Requirement, Customer Quote):\n\n${{corrected-requirements}}\n\n2. class diagram (PlantUML) :\n\n${{class-diagram}}\n\n3. sequence diagram (PlantUML) :\n\n${{sequence-diagram}}\n\n**Output Rules:** \n- **Language**: Python\n- **Architecture**: \n - 1:1 mapping of PlantUML classes to code \n - Sequence diagram steps → method logic \n- **Quality**: \n - Type hints (Python)\n - Docstrings referencing requirement IDs (e.g., `# F1`) \n - Appropriate error handling \n- **Format**:\n - Output only executable code, do not add any text, besides comments\n\n**Example Output (Python Calculator):** \n\nfrom enum import Enum, auto\n\nclass OperationType(Enum):\n \"\"\"Supported operations (matches class diagram <<enum>>)\"\"\"\n ADD = auto() # F3: Basic arithmetic\n SUBTRACT = auto() # F3\n MULTIPLY = auto() # F3\n DIVIDE = auto() # F3\n\nclass Calculator:\n \"\"\"Core calculator logic (from class diagram)\"\"\"\n def __init__(self):\n self.current_input = 0 # F1: Digit entry\n self.stored_value = None # For multi-step operations\n self.selected_op = None # F3: Operation selection\n \n def calculate(self) -> float:\n \"\"\"Execute the current operation (sequence diagram '=' press)\"\"\"\n if not self.selected_op:\n return self.current_input\n \n # Sequence diagram compute step\n match self.selected_op:\n case OperationType.ADD:\n result = self.stored_value + self.current_input\n case OperationType.SUBTRACT:\n result = self.stored_value - self.current_input\n # ... other operations (F3)\n \n self.current_input = result\n return result\n\nclass InputHandler:\n \"\"\"Manages user input flow (sequence diagram interactions)\"\"\"\n def __init__(self, calculator: Calculator, display: Display):\n self.calc = calculator\n self.display = display\n \n def handle_digit(self, digit: int): # F1\n \"\"\"Process digit press (sequence diagram step 1)\"\"\"\n self.calc.current_input = self.calc.current_input * 10 + digit\n self.display.update(str(self.calc.current_input))\n\n def handle_operator(self, op: OperationType): # F3\n \"\"\"Process +,-,*,/ press (sequence diagram step 2)\"\"\"\n self.calc.stored_value = self.calc.current_input\n self.calc.selected_op = op\n self.calc.current_input = 0\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue