Skip to content

Commit

Permalink
[frontend/backend] Chaining injects logically (#1194)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimfacion authored Sep 13, 2024
1 parent 8d75ef9 commit 250b2f8
Show file tree
Hide file tree
Showing 25 changed files with 1,057 additions and 287 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.openbas.migration;

import org.flywaydb.core.api.migration.BaseJavaMigration;
import org.flywaydb.core.api.migration.Context;
import org.springframework.stereotype.Component;

import java.sql.Statement;

@Component
public class V3_34__Remove_cascade_delete_dependency extends BaseJavaMigration {

@Override
public void migrate(Context context) throws Exception {
Statement select = context.getConnection().createStatement();
select.execute("ALTER TABLE injects DROP CONSTRAINT fk_depends_from_another;");
select.execute("ALTER TABLE injects ADD CONSTRAINT fk_depends_from_another " +
"FOREIGN KEY (inject_depends_from_another) REFERENCES injects ON DELETE SET NULL;");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class InjectInput {
@JsonProperty("inject_content")
private ObjectNode content;

@JsonProperty("inject_depends_from_another")
@JsonProperty("inject_depends_on")
private String dependsOn;

@JsonProperty("inject_depends_duration")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import jakarta.annotation.Resource;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import lombok.RequiredArgsConstructor;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
Expand All @@ -29,20 +30,24 @@

@Component
@DisallowConcurrentExecution
@RequiredArgsConstructor
public class InjectsExecutionJob implements Job {

private static final Logger LOGGER = Logger.getLogger(InjectsExecutionJob.class.getName());

private ApplicationContext context;
private InjectHelper injectHelper;
private DryInjectRepository dryInjectRepository;
private InjectRepository injectRepository;
private InjectorRepository injectorRepository;
private InjectStatusRepository injectStatusRepository;
private DryInjectStatusRepository dryInjectStatusRepository;
private ExerciseRepository exerciseRepository;
private QueueService queueService;
private ExecutionExecutorService executionExecutorService;
private final ApplicationContext context;
private final InjectHelper injectHelper;
private final DryInjectRepository dryInjectRepository;
private final InjectRepository injectRepository;
private final InjectorRepository injectorRepository;
private final InjectStatusRepository injectStatusRepository;
private final DryInjectStatusRepository dryInjectStatusRepository;
private final ExerciseRepository exerciseRepository;
private final QueueService queueService;
private final ExecutionExecutorService executionExecutorService;

private final List<ExecutionStatus> executionStatusesNotReady =
List.of(ExecutionStatus.QUEUING, ExecutionStatus.DRAFT, ExecutionStatus.EXECUTING, ExecutionStatus.PENDING);

@Resource
protected ObjectMapper mapper;
Expand All @@ -55,56 +60,6 @@ public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}

@Autowired
public void setQueueService(QueueService queueService) {
this.queueService = queueService;
}

@Autowired
public void setExecutionExecutorService(ExecutionExecutorService executionExecutorService) {
this.executionExecutorService = executionExecutorService;
}

@Autowired
public void setDryInjectStatusRepository(DryInjectStatusRepository dryInjectStatusRepository) {
this.dryInjectStatusRepository = dryInjectStatusRepository;
}

@Autowired
public void setInjectStatusRepository(InjectStatusRepository injectStatusRepository) {
this.injectStatusRepository = injectStatusRepository;
}

@Autowired
public void setInjectorRepository(InjectorRepository injectorRepository) {
this.injectorRepository = injectorRepository;
}

@Autowired
public void setInjectRepository(InjectRepository injectRepository) {
this.injectRepository = injectRepository;
}

@Autowired
public void setDryInjectRepository(DryInjectRepository dryInjectRepository) {
this.dryInjectRepository = dryInjectRepository;
}

@Autowired
public void setExerciseRepository(ExerciseRepository exerciseRepository) {
this.exerciseRepository = exerciseRepository;
}

@Autowired
public void setContext(ApplicationContext context) {
this.context = context;
}

@Autowired
public void setInjectHelper(InjectHelper injectHelper) {
this.injectHelper = injectHelper;
}

public void handleAutoStartExercises() {
List<Exercise> exercises = exerciseRepository.findAllShouldBeInRunningState(now());
exerciseRepository.saveAll(exercises.stream()
Expand Down Expand Up @@ -190,57 +145,77 @@ private void executeInject(ExecutableInject executableInject) {
// Depending on injector type (internal or external) execution must be done differently
Inject inject = executableInject.getInjection().getInject();

inject.getInjectorContract().ifPresent(injectorContract -> {

if (!inject.isReady()) {
// Status
if (inject.getStatus().isEmpty()) {
InjectStatus status = new InjectStatus();
status.getTraces().add(InjectStatusExecution.traceError("The inject is not ready to be executed (missing mandatory fields)"));
status.setName(ExecutionStatus.ERROR);
status.setTrackingSentDate(Instant.now());
status.setInject(inject);
injectStatusRepository.save(status);
} else {
InjectStatus status = inject.getStatus().get();
status.getTraces().add(InjectStatusExecution.traceError("The inject is not ready to be executed (missing mandatory fields)"));
status.setName(ExecutionStatus.ERROR);
status.setTrackingSentDate(Instant.now());
injectStatusRepository.save(status);
}
return;
// We are now checking if we depend on another inject and if it did not failed
if (inject.getDependsOn() != null
&& inject.getDependsOn().getStatus().isPresent()
&& ( inject.getDependsOn().getStatus().get().getName().equals(ExecutionStatus.ERROR)
|| executionStatusesNotReady.contains(inject.getDependsOn().getStatus().get().getName()))) {
InjectStatus status = new InjectStatus();
if (inject.getStatus().isEmpty()) {
status.setInject(inject);
} else {
status = inject.getStatus().get();
}

Injector externalInjector = injectorRepository.findByType(injectorContract.getInjector().getType()).orElseThrow();
LOGGER.log(Level.INFO, "Executing inject " + inject.getInject().getTitle());
// Executor logics
ExecutableInject newExecutableInject = executableInject;
if (Boolean.TRUE.equals(injectorContract.getNeedsExecutor())) {
try {
String errorMsg = inject.getDependsOn().getStatus().get().getName().equals(ExecutionStatus.ERROR) ?
"The inject is depending on another inject that failed"
: "The inject is depending on another inject that is not executed yet";
status.getTraces().add(InjectStatusExecution.traceError(errorMsg));
status.setName(ExecutionStatus.ERROR);
status.setTrackingSentDate(Instant.now());
injectStatusRepository.save(status);
} else {
inject.getInjectorContract().ifPresent(injectorContract -> {

if (!inject.isReady()) {
// Status
if (inject.getStatus().isEmpty()) {
InjectStatus status = new InjectStatus();
status.setName(ExecutionStatus.EXECUTING);
status.getTraces().add(InjectStatusExecution.traceError("The inject is not ready to be executed (missing mandatory fields)"));
status.setName(ExecutionStatus.ERROR);
status.setTrackingSentDate(Instant.now());
status.setInject(inject);
injectStatusRepository.save(status);
} else {
InjectStatus status = inject.getStatus().get();
status.setName(ExecutionStatus.EXECUTING);
status.getTraces().add(InjectStatusExecution.traceError("The inject is not ready to be executed (missing mandatory fields)"));
status.setName(ExecutionStatus.ERROR);
status.setTrackingSentDate(Instant.now());
injectStatusRepository.save(status);
}
newExecutableInject = this.executionExecutorService.launchExecutorContext(executableInject, inject);
} catch (InterruptedException e) {
throw new RuntimeException(e);
return;
}
}
if (externalInjector.isExternal()) {
executeExternal(newExecutableInject);
} else {
executeInternal(newExecutableInject);
}
});

Injector externalInjector = injectorRepository.findByType(injectorContract.getInjector().getType()).orElseThrow();
LOGGER.log(Level.INFO, "Executing inject " + inject.getInject().getTitle());
// Executor logics
ExecutableInject newExecutableInject = executableInject;
if (Boolean.TRUE.equals(injectorContract.getNeedsExecutor())) {
try {
// Status
if (inject.getStatus().isEmpty()) {
InjectStatus status = new InjectStatus();
status.setName(ExecutionStatus.EXECUTING);
status.setTrackingSentDate(Instant.now());
status.setInject(inject);
injectStatusRepository.save(status);
} else {
InjectStatus status = inject.getStatus().get();
status.setName(ExecutionStatus.EXECUTING);
status.setTrackingSentDate(Instant.now());
injectStatusRepository.save(status);
}
newExecutableInject = this.executionExecutorService.launchExecutorContext(executableInject, inject);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
if (externalInjector.isExternal()) {
executeExternal(newExecutableInject);
} else {
executeInternal(newExecutableInject);
}
});
}
}

public void updateExercise(String exerciseId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ public Exercise toExercise(

// Injects
List<Inject> scenarioInjects = scenario.getInjects();
Map<String, Inject> mapExerciseInjectsByScenarioInject = new HashMap<>();
scenarioInjects.forEach(scenarioInject -> {
Inject exerciseInject = new Inject();
exerciseInject.setTitle(scenarioInject.getTitle());
Expand Down Expand Up @@ -228,6 +229,8 @@ public Exercise toExercise(
exerciseInject.setAssetGroups(CopyObjectListUtils.copy(scenarioInject.getAssetGroups(), AssetGroup.class));
Inject injectSaved = this.injectRepository.save(exerciseInject);

mapExerciseInjectsByScenarioInject.put(scenarioInject.getId(), injectSaved);

// Documents
List<InjectDocument> exerciseInjectDocuments = new ArrayList<>();
scenarioInject.getDocuments().forEach(injectDocument -> {
Expand All @@ -240,6 +243,15 @@ public Exercise toExercise(
this.injectDocumentRepository.saveAll(exerciseInjectDocuments);
});

// Second pass to add the correct links
scenarioInjects.forEach(scenarioInject -> {
if(scenarioInject.getDependsOn() != null) {
Inject injectToUpdate = mapExerciseInjectsByScenarioInject.get(scenarioInject.getId());
injectToUpdate.setDependsOn(mapExerciseInjectsByScenarioInject.get(scenarioInject.getDependsOn().getId()));
this.injectRepository.save(injectToUpdate);
}
});

// Variables
List<Variable> scenarioVariables = this.variableService.variablesFromScenario(scenario.getId());
List<Variable> exerciseVariables = scenarioVariables.stream()
Expand Down
2 changes: 1 addition & 1 deletion openbas-front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@mui/x-date-pickers": "7.12.0",
"@redux-devtools/extension": "3.3.0",
"@uiw/react-md-editor": "4.0.4",
"@xyflow/react": "^12.0.3",
"@xyflow/react": "12.2.1",
"apexcharts": "3.51.0",
"axios": "1.7.4",
"ckeditor5-custom-build": "link:packages/ckeditor5-custom-build",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ const TargetResultsDetailFlow: FunctionComponent<Props> = ({
</div>
<div className={classes.container} style={{ width: '100%', height: 150 }}>
<ReactFlow
colorMode={theme.palette.mode}
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,20 @@ const NodeResultStepComponent = ({ data }: NodeProps<NodeResultStep>) => {
<Tooltip title={data.description}>
<div className={classes.description}>{data.description}</div>
</Tooltip>
{(data.end || data.middle) && (<Handle type="target" position={Position.Left} isConnectable={false} />)}
{(data.start || data.middle) && (<Handle type="source" position={Position.Right} isConnectable={false} />)}
{(data.end || data.middle) && (
<Handle
type="target"
position={Position.Left}
isConnectable={false}
/>
)}
{(data.start || data.middle) && (
<Handle
type="source"
position={Position.Right}
isConnectable={false}
/>
)}
</div>
);
};
Expand Down
1 change: 1 addition & 0 deletions openbas-front/src/admin/components/common/Context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,4 @@ export const ViewLessonContext = createContext<ViewLessonContextType>({
});
},
});
export const ViewModeContext = createContext('list');
Loading

0 comments on commit 250b2f8

Please sign in to comment.