All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.resys.hdes.client.spi.envir.AssociationVisitor Maven / Gradle / Ivy

There is a newer version: 3.130.78
Show newest version
package io.resys.hdes.client.spi.envir;

/*-
 * #%L
 * hdes-client-api
 * %%
 * Copyright (C) 2020 - 2021 Copyright 2020 ReSys OÜ
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import io.resys.hdes.client.api.ast.AstBody.AstBodyType;
import io.resys.hdes.client.api.ast.AstBody.AstSource;
import io.resys.hdes.client.api.ast.AstBranch;
import io.resys.hdes.client.api.ast.AstDecision;
import io.resys.hdes.client.api.ast.AstDecision.HitPolicy;
import io.resys.hdes.client.api.ast.AstFlow;
import io.resys.hdes.client.api.ast.AstService;
import io.resys.hdes.client.api.ast.AstTag;
import io.resys.hdes.client.api.programs.BranchProgram;
import io.resys.hdes.client.api.programs.DecisionProgram;
import io.resys.hdes.client.api.programs.FlowProgram;
import io.resys.hdes.client.api.programs.FlowProgram.FlowProgramStep;
import io.resys.hdes.client.api.programs.FlowProgram.FlowProgramStepRefType;
import io.resys.hdes.client.api.programs.ImmutableProgramAssociation;
import io.resys.hdes.client.api.programs.ImmutableProgramMessage;
import io.resys.hdes.client.api.programs.ImmutableProgramWrapper;
import io.resys.hdes.client.api.programs.ProgramEnvir.ProgramAssociation;
import io.resys.hdes.client.api.programs.ProgramEnvir.ProgramMessage;
import io.resys.hdes.client.api.programs.ProgramEnvir.ProgramStatus;
import io.resys.hdes.client.api.programs.ProgramEnvir.ProgramWrapper;
import io.resys.hdes.client.api.programs.ServiceProgram;
import io.resys.hdes.client.api.programs.TagProgram;
import io.resys.hdes.client.spi.flow.validators.FlowAssociationValidator;
import io.resys.hdes.client.spi.util.HdesAssert;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class AssociationVisitor {
  private final Map externalIdToSource = new HashMap<>();
  private final Map> typeToExternalId = new HashMap<>(Map.of(
        AstBodyType.DT, new ArrayList(),
        AstBodyType.FLOW_TASK, new ArrayList(),
        AstBodyType.FLOW, new ArrayList(),
        AstBodyType.TAG, new ArrayList(),
        AstBodyType.BRANCH, new ArrayList()
      ));
  private final Map> externalIdToDependencyErrors = new HashMap<>();
  private final Map> externalIdToDependencyWarnings = new HashMap<>();
  private final Map> externalIdToWrapper = new HashMap<>();
  private final Map> externalIdToAssoc = new HashMap<>();

  public AssociationVisitor add(ProgramWrapper wrapper) {
    HdesAssert.isTrue(!this.externalIdToSource.containsKey(wrapper.getId()), () -> "commandJson with id: '" + wrapper.getId() + "' is already defined!");
    externalIdToSource.put(wrapper.getId(), wrapper.getSource());
    typeToExternalId.get(wrapper.getType()).add(wrapper.getId());
    externalIdToDependencyErrors.put(wrapper.getId(), new ArrayList<>());
    externalIdToDependencyWarnings.put(wrapper.getId(), new ArrayList<>());
    externalIdToWrapper.put(wrapper.getId(), wrapper);
    return this;
  }
  
  
  @SuppressWarnings("unchecked")
  public Collection> build() {

    typeToExternalId.get(AstBodyType.FLOW).stream()
      .map(id -> (ProgramWrapper) externalIdToWrapper.get(id))
      .forEach(this::visitFlowProgramAssociation);
    
    typeToExternalId.get(AstBodyType.FLOW_TASK).stream()
      .map(id -> (ProgramWrapper) externalIdToWrapper.get(id))
      .forEach(this::visitTaskProgramAssociation);
    
    
    return externalIdToWrapper.values().stream()
        .map(this::visitWrapper)
        .collect(Collectors.toList());
  }
  
  
  private void visitTaskProgramAssociation(ProgramWrapper wrapper) {
    if(wrapper.getProgram().isEmpty()) {
      return;
    }
    
    if(!externalIdToAssoc.containsKey(wrapper.getId())) {
      externalIdToAssoc.put(wrapper.getId(), new ArrayList());
    }
    
    final var result = externalIdToAssoc.get(wrapper.getId());
    for(final var entry : wrapper.getAst().get().getRefs()) {
      final var refWrapper = externalIdToWrapper.values().stream()
        .filter(w -> w.getSource().getBodyType() == entry.getBodyType())
        .filter(w -> entry.getRefValue().equals(w.getAst().map(a -> a.getName()).orElseGet(null)))
        .findFirst().orElse(null);

      
      if(refWrapper == null) {
        continue;
      }
      

      if(refWrapper.getStatus() != ProgramStatus.UP) {
        externalIdToDependencyErrors.get(wrapper.getId()).add(ImmutableProgramMessage.builder()
            .id("dependency-error")
            .msg("Dependency for ref: '" + wrapper.getAst().get().getName() + "/" + entry.getRefValue() + "' with id: '" + refWrapper.getId() + "' has a ERROR!")
            .build());
        
        result.add(ImmutableProgramAssociation.builder()
            .id(refWrapper.getId())
            .ref(refWrapper.getAst().get().getName())
            .refType(entry.getBodyType())
            .owner(true)
            .refStatus(ProgramStatus.DEPENDENCY_ERROR)
            .build());
        

      } else {
        result.add(ImmutableProgramAssociation.builder()
            .id(refWrapper.getId())
            .ref(refWrapper.getAst().get().getName())
            .refType(entry.getBodyType())
            .owner(true)
            .refStatus(refWrapper.getStatus())
            .build());      
      }
      
      
    }
  }
    
  private void visitFlowProgramAssociation(ProgramWrapper wrapper) {
    if(wrapper.getProgram().isEmpty()) {
      return;
    }
    
    if(externalIdToAssoc.containsKey(wrapper.getId())) {
      return;
    }
    externalIdToAssoc.put(wrapper.getId(), new ArrayList());
    
    final var program = wrapper.getProgram().get();
    try {
      final var validator = new FlowAssociationValidator(wrapper.getAst().get());
      program.getSteps().values().forEach(step -> visitFlowStep(wrapper, step, validator));
      
      for(final var entry : validator.build()) {
        if(entry.getMessages().isEmpty()) {
          continue;
        }
        
        final var step = entry.getStep();
        if(step != null) {
          final var ref = entry.getStep().getBody().getRef();
          
          entry.getMessages().forEach(msg -> {
            final var progMsg = ImmutableProgramMessage.builder().id("dependency-error").msg("line: " + msg.getLine() + ": " + msg.getValue()).build();          
            externalIdToDependencyErrors.get(wrapper.getId()).add(progMsg);
          });
          
          final var allAssoc = externalIdToAssoc.get(wrapper.getId());          
          final var assoc = allAssoc.stream().filter(a -> a.getRef().equals(ref)).findFirst();
          allAssoc.remove(assoc.get());
          allAssoc.add(ImmutableProgramAssociation.builder()
              .from(assoc.get())
              .refStatus(ProgramStatus.DEPENDENCY_ERROR)
              .build());
        } else {
          entry.getMessages().forEach(msg -> {
            final var progMsg = ImmutableProgramMessage.builder().id("dependency-warning").msg("line: " + msg.getLine() + ": " + msg.getValue()).build();          
            externalIdToDependencyWarnings.get(wrapper.getId()).add(progMsg);
          });
        } 
      }
    } catch(Exception e) {
      log.error("Failed to validate FLOW: " + wrapper.getId() + ", " + e.getMessage(), e);
    }
  }
  
  private void visitFlowStep(ProgramWrapper wrapper, FlowProgramStep step, FlowAssociationValidator validator) {
    if(step.getBody() == null) {
      return;
    } 
    final var result = externalIdToAssoc.get(wrapper.getId());
    final var refWrapper = visitRefWrapper(wrapper, step);
  
    if(refWrapper == null) {
      return;
    }
    
    if(refWrapper.getType() == AstBodyType.DT) {
      AstDecision decision = (AstDecision) refWrapper.getAst().get();
      Boolean collection = decision.getHitPolicy() == HitPolicy.ALL;
      if(!step.getBody().getCollection().equals(collection)) {
        result.add(ImmutableProgramAssociation.builder()
            .ref(step.getBody().getRef())
            .refType(AstBodyType.DT)
            .owner(true)
            .refStatus(ProgramStatus.DEPENDENCY_ERROR)
            .build());
        externalIdToDependencyErrors.get(wrapper.getId()).add(ImmutableProgramMessage.builder()
            .id("dependency-error")
            .msg("Dependency for ref: '" + step.getId() + "/" + step.getBody().getRef() + "'"
                + " with id: '" + refWrapper.getId() + "'"
                + " has collection: '" + collection + "'"
                + " but flow has: '" + step.getBody().getCollection() + "'!")
            .build());
      }
    }
    validator.visitStep(step, refWrapper);
  }
  
  private ProgramWrapper visitRefWrapper(ProgramWrapper wrapper, FlowProgramStep step) {
    List refs = Collections.emptyList();
    final var refType = step.getBody().getRefType();
    final AstBodyType bodyType;
    if(refType == FlowProgramStepRefType.SERVICE) {
      bodyType = AstBodyType.FLOW_TASK;
      refs = typeToExternalId.get(AstBodyType.FLOW_TASK);
    } else if(refType == FlowProgramStepRefType.DT) {
      bodyType = AstBodyType.DT;
      refs = typeToExternalId.get(AstBodyType.DT);
    } else {
      bodyType = AstBodyType.FLOW;
    }
    
    final var result = externalIdToAssoc.get(wrapper.getId());
    final var ref = refs.stream()
        .map(id -> externalIdToWrapper.get(id))
        .filter(w -> w.getAst().isPresent())
        .filter(w -> w.getAst().get().getName().equals(step.getBody().getRef()))
        .collect(Collectors.toList());
    
    if(ref.isEmpty()) {
      result.add(ImmutableProgramAssociation.builder()
          .ref(step.getBody().getRef())
          .refType(bodyType)
          .owner(true)
          .refStatus(ProgramStatus.DEPENDENCY_ERROR)
          .build());
      externalIdToDependencyErrors.get(wrapper.getId()).add(ImmutableProgramMessage.builder()
          .id("dependency-error")
          .msg("Missing dependency for ref: '" + step.getId() + "/" + step.getBody().getRef() + "'!")
          .build());
      
      return null;
    } else if(ref.size() > 1) {
      result.add(ImmutableProgramAssociation.builder()
          .owner(true)
          .ref(step.getBody().getRef())
          .refStatus(ProgramStatus.DEPENDENCY_ERROR)
          .refType(bodyType)
          .build());
      final var deps = String.join(",", ref.stream().map(e -> e.getId()).collect(Collectors.toList()));
      externalIdToDependencyErrors.get(wrapper.getId()).add(ImmutableProgramMessage.builder()
          .id("dependency-error")
          .msg("Found '" + ref.size() + "'" + 
               " ref: '" + step.getId() + "/" + step.getBody().getRef() + "'" + 
               " dependencies: '" + deps + "' instead of 1!")
          .build());
      
      return null;
    }
    
    final var refWrapper = ref.get(0);
    if(refWrapper.getStatus() != ProgramStatus.UP) {
      externalIdToDependencyErrors.get(wrapper.getId()).add(ImmutableProgramMessage.builder()
          .id("dependency-error")
          .msg("Dependency for ref: '" + step.getId() + "/" + step.getBody().getRef() + "' with id: '" + refWrapper.getId() + "' has a ERROR!")
          .build());
      
      result.add(ImmutableProgramAssociation.builder()
          .id(refWrapper.getId())
          .ref(refWrapper.getAst().get().getName())
          .refType(bodyType)
          .owner(true)
          .refStatus(ProgramStatus.DEPENDENCY_ERROR)
          .build());
      
      return null;
    } else {
      result.add(ImmutableProgramAssociation.builder()
          .id(refWrapper.getId())
          .ref(refWrapper.getAst().get().getName())
          .refType(bodyType)
          .owner(true)
          .refStatus(refWrapper.getStatus())
          .build());      
    }
    
    // Add dependency to ref
    var refsAssocs = externalIdToAssoc.get(refWrapper.getId());
    if(refsAssocs == null) {
      refsAssocs = new ArrayList<>();
      externalIdToAssoc.put(refWrapper.getId(), refsAssocs);
    }
    
    refsAssocs.add(ImmutableProgramAssociation.builder()
      .id(wrapper.getId())
      .ref(wrapper.getAst().get().getName())
      .refType(wrapper.getType())
      .owner(false)
      .refStatus(wrapper.getStatus())
      .build());
    
    
    return refWrapper;
  }
  
  @SuppressWarnings("unchecked")
  private ProgramWrapper visitWrapper(ProgramWrapper wrapper) {
    switch (wrapper.getType()) {
    case DT: {
      final ImmutableProgramWrapper.Builder builder = ImmutableProgramWrapper.builder();
      final var assoc = externalIdToAssoc.get(wrapper.getId());
      final var errors = externalIdToDependencyErrors.get(wrapper.getId());
      return builder
          .from((ProgramWrapper) wrapper)
          .associations(assoc == null ? Collections.emptyList() : assoc)
          .status(errors.isEmpty() ? wrapper.getStatus() : ProgramStatus.DEPENDENCY_ERROR)
          .addAllErrors(externalIdToDependencyErrors.get(wrapper.getId()))
          .addAllErrors(errors)
          .addAllWarnings(externalIdToDependencyWarnings.get(wrapper.getId()))
          .build();
    }
    case FLOW_TASK: {
      final ImmutableProgramWrapper.Builder builder = ImmutableProgramWrapper.builder();
      final var assoc = externalIdToAssoc.get(wrapper.getId());
      final var errors = externalIdToDependencyErrors.get(wrapper.getId());
      return builder
          .from((ProgramWrapper) wrapper)
          .associations(assoc == null ? Collections.emptyList() : assoc)
          .status(errors.isEmpty() ? wrapper.getStatus() : ProgramStatus.DEPENDENCY_ERROR)
          .addAllErrors(externalIdToDependencyErrors.get(wrapper.getId()))
          .addAllErrors(errors)
          .addAllWarnings(externalIdToDependencyWarnings.get(wrapper.getId()))
          .build();
    }
    case FLOW: {
      final ImmutableProgramWrapper.Builder builder = ImmutableProgramWrapper.builder();
      final var assoc = externalIdToAssoc.get(wrapper.getId());
      final var errors = externalIdToDependencyErrors.get(wrapper.getId());
      
      return builder
          .from((ProgramWrapper) wrapper)
          .associations(assoc == null ? Collections.emptyList() : assoc)
          .status(errors.isEmpty() ? wrapper.getStatus() : ProgramStatus.DEPENDENCY_ERROR)
          .addAllErrors(externalIdToDependencyErrors.get(wrapper.getId()))
          .addAllErrors(errors)
          .addAllWarnings(externalIdToDependencyWarnings.get(wrapper.getId()))
          .build();
    }
    case TAG: {
      final ImmutableProgramWrapper.Builder builder = ImmutableProgramWrapper.builder();
      final var assoc = externalIdToAssoc.get(wrapper.getId());
      final var errors = externalIdToDependencyErrors.get(wrapper.getId());
      
      return builder
          .from((ProgramWrapper) wrapper)
          .associations(assoc == null ? Collections.emptyList() : assoc)
          .status(errors.isEmpty() ? wrapper.getStatus() : ProgramStatus.DEPENDENCY_ERROR)
          .addAllErrors(externalIdToDependencyErrors.get(wrapper.getId()))
          .addAllErrors(errors)
          .addAllWarnings(externalIdToDependencyWarnings.get(wrapper.getId()))
          .build();
    }
      case BRANCH: {
        final ImmutableProgramWrapper.Builder builder = ImmutableProgramWrapper.builder();
        final var assoc = externalIdToAssoc.get(wrapper.getId());
        final var errors = externalIdToDependencyErrors.get(wrapper.getId());

        return builder
            .from((ProgramWrapper) wrapper)
            .associations(assoc == null ? Collections.emptyList() : assoc)
            .status(errors.isEmpty() ? wrapper.getStatus() : ProgramStatus.DEPENDENCY_ERROR)
            .addAllErrors(externalIdToDependencyErrors.get(wrapper.getId()))
            .addAllErrors(errors)
            .addAllWarnings(externalIdToDependencyWarnings.get(wrapper.getId()))
            .build();
      }
    default: throw new IllegalArgumentException("unknown command format type: '" + wrapper.getType() + "'!");
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy