Skip to content

Commit c1b6e47

Browse files
authored
Merge pull request #28 from API-Flows/validate-step-metadata
Validate step metadata
2 parents 06e3fb4 + 42b06b0 commit c1b6e47

File tree

3 files changed

+194
-17
lines changed

3 files changed

+194
-17
lines changed

src/main/java/com/apiflows/model/SourceDescription.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public boolean isOpenApi() {
3939
return "openapi".equals(this.type);
4040
}
4141

42-
public boolean IsWorkflowsSpec() {
42+
public boolean isWorkflowsSpec() {
4343
return "workflowsSpec".equals(this.type);
4444
}
4545

src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class OpenAPIWorkflowValidator {
1212
private OpenAPIWorkflow openAPIWorkflow = null;
1313
Set<String> workflowIds = new HashSet<>();
1414
Map<String, Set<String>> stepIds = new HashMap<>();
15+
Set<String> operationIds = new HashSet<>();
1516
Set<Schema> components = new HashSet<>();
1617

1718
OpenAPIWorkflowValidator() {
@@ -27,8 +28,9 @@ public OpenAPIWorkflowValidatorResult validate() {
2728
throw new RuntimeException("OpenAPIWorkflow is not provided");
2829
}
2930

30-
loadWorkflowIds(this.openAPIWorkflow.getWorkflows());
31+
loadWorkflowIds(this.openAPIWorkflow);
3132
loadStepIds(this.openAPIWorkflow.getWorkflows());
33+
loadOperationIds(this.openAPIWorkflow);
3234

3335
OpenAPIWorkflowValidatorResult result = new OpenAPIWorkflowValidatorResult();
3436

@@ -346,8 +348,8 @@ List<String> validateComponents(Components components) {
346348
if (components.getParameters() != null) {
347349

348350
for(String key : components.getParameters().keySet()) {
349-
if(isValidComponentKey(key)) {
350-
errors.add("'Component parameter " + key + " is invalid (should match regex " + getComponentKeyRegularExpression() + ")");
351+
if(!isValidComponentKey(key)) {
352+
errors.add("'Component parameter name " + key + " is invalid (should match regex " + getComponentKeyRegularExpression() + ")");
351353
}
352354
}
353355

@@ -356,13 +358,11 @@ List<String> validateComponents(Components components) {
356358
}
357359
}
358360
if (components.getInputs() != null) {
359-
360361
for(String key : components.getInputs().keySet()) {
361-
if(isValidComponentKey(key)) {
362+
if(!isValidComponentKey(key)) {
362363
errors.add("'Component input " + key + " is invalid (should match regex " + getComponentKeyRegularExpression() + ")");
363364
}
364365
}
365-
366366
}
367367
}
368368

@@ -400,18 +400,24 @@ String getOutputsKeyRegularExpression() {
400400
return "^[a-zA-Z0-9\\.\\-_]+$";
401401
}
402402

403-
List<String> loadWorkflowIds(List<Workflow> workflows) {
403+
List<String> loadWorkflowIds(OpenAPIWorkflow openAPIWorkflow) {
404404
List<String> errors = new ArrayList<>();
405405

406-
if(workflows != null) {
407-
for(Workflow workflow : workflows) {
408-
if(!this.workflowIds.add(workflow.getWorkflowId())) {
409-
// id already exists
410-
errors.add("WorkflowId is not unique: " + workflow.getWorkflowId());
411-
}
406+
boolean multipleWorkflowsSpec = getNumWorkflowsSpecSourceDescriptions(openAPIWorkflow.getSourceDescriptions()) > 1 ? true : false;
407+
408+
409+
if(openAPIWorkflow.getWorkflows() != null) {
410+
validateWorkflowIdsUniqueness(openAPIWorkflow.getWorkflows());
411+
412+
for (Workflow workflow : openAPIWorkflow.getWorkflows()) {
413+
errors.addAll(validateStepsWorkflowIds(workflow.getSteps(), multipleWorkflowsSpec));
412414
}
413-
}
414415

416+
for (Workflow workflow : openAPIWorkflow.getWorkflows()) {
417+
this.workflowIds.add(workflow.getWorkflowId());
418+
}
419+
420+
}
415421
return errors;
416422
}
417423

@@ -440,10 +446,82 @@ List<String> loadStepIds(List<Workflow> workflows) {
440446
return errors;
441447
}
442448

449+
List<String> loadOperationIds(OpenAPIWorkflow openAPIWorkflow) {
450+
List<String> errors = new ArrayList<>();
451+
452+
boolean multipleOpenApiFiles = getNumOpenApiSourceDescriptions(openAPIWorkflow.getSourceDescriptions()) > 1 ? true : false;
453+
454+
for(Workflow workflow : openAPIWorkflow.getWorkflows()) {
455+
errors.addAll(validateStepsOperationIds(workflow.getSteps(), multipleOpenApiFiles));
456+
457+
for(Step step : workflow.getSteps()) {
458+
if(step.getOperationId() != null) {
459+
this.operationIds.add(step.getOperationId());
460+
}
461+
}
462+
}
463+
464+
return errors;
465+
}
466+
467+
public List<String> validateStepsOperationIds(List<Step> steps, boolean multipleOpenApiFiles) {
468+
List<String> errors = new ArrayList<>();
469+
470+
for(Step step : steps) {
471+
if(multipleOpenApiFiles) {
472+
// must use runtime expression to map applicable SourceDescription
473+
if(step.getOperationId() != null && !step.getOperationId().startsWith("$sourceDescriptions.")) {
474+
errors.add("Operation " + step.getOperationId() + " must be specified using a runtime expression (e.g., $sourceDescriptions.<name>.<operationId>)");
475+
}
476+
}
477+
}
478+
479+
return errors;
480+
}
481+
482+
// num of SourceDescriptions with type 'openapi'
483+
int getNumOpenApiSourceDescriptions(List<SourceDescription> sourceDescriptions) {
484+
return (int) sourceDescriptions.stream().filter(p -> p.isOpenApi()).count();
485+
}
486+
487+
// num of SourceDescriptions with type 'workflowsSpec'
488+
int getNumWorkflowsSpecSourceDescriptions(List<SourceDescription> sourceDescriptions) {
489+
return (int) sourceDescriptions.stream().filter(p -> p.isWorkflowsSpec()).count();
490+
}
491+
443492
boolean stepExists(String workflowId, String stepId) {
444493
return this.stepIds.get(workflowId) != null && this.stepIds.get(workflowId).contains(stepId);
445494
}
446495

496+
List<String> validateWorkflowIdsUniqueness(List<Workflow> workflows) {
497+
List<String> errors = new ArrayList<>();
498+
499+
Set<String> ids = new HashSet<>();
500+
for(Workflow workflow : workflows) {
501+
if(!ids.add(workflow.getWorkflowId())) {
502+
// id already exists
503+
errors.add("WorkflowId is not unique: " + workflow.getWorkflowId());
504+
}
505+
}
506+
return errors;
507+
}
508+
509+
List<String> validateStepsWorkflowIds(List<Step> steps, boolean multipleWorkflowsSpecFiles) {
510+
List<String> errors = new ArrayList<>();
511+
512+
for(Step step : steps) {
513+
if(multipleWorkflowsSpecFiles) {
514+
// must use runtime expression to map applicable SourceDescription
515+
if(step.getWorkflowId() != null && !step.getWorkflowId().startsWith("$sourceDescriptions.")) {
516+
errors.add("Operation " + step.getWorkflowId() + " must be specified using a runtime expression (e.g., $sourceDescriptions.<name>.<workflowId>)");
517+
}
518+
}
519+
}
520+
521+
return errors;
522+
}
523+
524+
447525
public boolean isValidJsonPointer(String jsonPointerString) {
448526

449527
boolean ret;

src/test/java/com/apiflows/parser/OpenAPIWorkflowValidatorTest.java

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,14 +486,14 @@ void validateFailureActionInvalidStepId() {
486486

487487

488488
@Test
489-
void loadWorkflowIWithDuplicateIds() {
489+
void validateWorkflowIdsUniqueness() {
490490
List<Workflow> list = List.of(
491491
new Workflow()
492492
.workflowId("one"),
493493
new Workflow()
494494
.workflowId("one"));
495495

496-
assertEquals(1, validator.loadWorkflowIds(list).size());
496+
assertEquals(1, validator.validateWorkflowIdsUniqueness(list).size());
497497
}
498498

499499
@Test
@@ -510,6 +510,20 @@ void validateComponentsParameterInvalidIn() {
510510
assertEquals(1, validator.validateParameter(parameter, worklowId).size());
511511
}
512512

513+
@Test
514+
void validateComponentsParameter() {
515+
Parameter parameter = new Parameter()
516+
.name("page")
517+
.value("1")
518+
.in("query");
519+
String worklowId = "q1";
520+
521+
Components components = new Components();
522+
components.addParameter("page", parameter);
523+
524+
assertEquals(0, validator.validateParameter(parameter, worklowId).size());
525+
}
526+
513527
@Test
514528
void loadStepsWithDuplicateIds() {
515529
List<Workflow> list = List.of(
@@ -524,6 +538,91 @@ void loadStepsWithDuplicateIds() {
524538
assertEquals(1, validator.loadStepIds(list).size());
525539
}
526540

541+
@Test
542+
public void getNumOpenApiSourceDescriptions() {
543+
List<SourceDescription> sourceDescriptions = List.of(
544+
new SourceDescription()
545+
.name("openapifile-1")
546+
.type("openapi"),
547+
new SourceDescription()
548+
.name("openapifile-2")
549+
.type("openapi"),
550+
new SourceDescription()
551+
.name("workflowspec-1")
552+
.type("workflowsSpec")
553+
);
554+
555+
assertEquals(2, new OpenAPIWorkflowValidator().getNumOpenApiSourceDescriptions(sourceDescriptions));
556+
}
557+
558+
@Test
559+
public void getNumWorkflowsSpecSourceDescriptions() {
560+
List<SourceDescription> sourceDescriptions = List.of(
561+
new SourceDescription()
562+
.name("openapifile-1")
563+
.type("openapi"),
564+
new SourceDescription()
565+
.name("openapifile-2")
566+
.type("openapi"),
567+
new SourceDescription()
568+
.name("workflowspec-1")
569+
.type("workflowsSpec")
570+
);
571+
572+
assertEquals(1, new OpenAPIWorkflowValidator().getNumWorkflowsSpecSourceDescriptions(sourceDescriptions));
573+
}
574+
575+
@Test
576+
public void validateStepsOperationIds() {
577+
boolean multipleOpenApiFiles = true;
578+
579+
List<Step> steps = List.of(
580+
new Step()
581+
.stepId("step-one")
582+
.operationId("post-operation")
583+
);
584+
585+
assertEquals(1, new OpenAPIWorkflowValidator().validateStepsOperationIds(steps, multipleOpenApiFiles).size());
586+
}
587+
588+
@Test
589+
public void validateStepsOperationIdsWithoutRuntimeExpression() {
590+
boolean multipleOpenApiFiles = false;
591+
592+
List<Step> steps = List.of(
593+
new Step()
594+
.stepId("step-one")
595+
.operationId("post-operation")
596+
);
597+
598+
assertEquals(0, new OpenAPIWorkflowValidator().validateStepsOperationIds(steps, multipleOpenApiFiles).size());
599+
}
600+
601+
@Test
602+
public void validateStepsWorkflowIds() {
603+
boolean multipleWorkflowsSpecFiles = true;
604+
605+
List<Step> steps = List.of(
606+
new Step()
607+
.stepId("step-one")
608+
.workflowId("w2")
609+
);
610+
611+
assertEquals(1, new OpenAPIWorkflowValidator().validateStepsWorkflowIds(steps, multipleWorkflowsSpecFiles).size());
612+
}
613+
614+
@Test
615+
public void validateStepsWorkflowIdsWithoutRuntimeExpression() {
616+
boolean multipleWorkflowsSpecFiles = false;
617+
618+
List<Step> steps = List.of(
619+
new Step()
620+
.stepId("step-one")
621+
.workflowId("w2")
622+
);
623+
624+
assertEquals(0, new OpenAPIWorkflowValidator().validateStepsWorkflowIds(steps, multipleWorkflowsSpecFiles).size());
625+
}
527626

528627
@Test
529628
void validWorkflowId() {

0 commit comments

Comments
 (0)