CMSgov/dpc-app

View on GitHub
dpc-common/src/main/java/gov/cms/dpc/fhir/validations/DPCProfileSupport.java

Summary

Maintainability
A
0 mins
Test Coverage
package gov.cms.dpc.fhir.validations;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import gov.cms.dpc.fhir.helpers.ServiceLoaderHelpers;
import gov.cms.dpc.fhir.validations.profiles.IProfileLoader;
import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * DPC specific implementation of FHIR's {@link IValidationSupport}, which allows us to load our own {@link StructureDefinition}s from the JAR.
 * <p>
 * Loading is done through Java's {@link ServiceLoader} feature, we declare profiles that implement {@link IProfileLoader} and then place them in the corresponding file under META-INF/services
 */
public class DPCProfileSupport implements IValidationSupport {

    private static final Logger logger = LoggerFactory.getLogger(DPCProfileSupport.class);

    private final Map<String, StructureDefinition> structureMap;

    @Inject
    public DPCProfileSupport(FhirContext ctx) {
        this.structureMap = loadProfiles(ctx);
    }

    @Override
    public ValueSet.ValueSetExpansionComponent expandValueSet(FhirContext theContext, ValueSet.ConceptSetComponent theInclude) {
        return null;
    }

    @Override
    public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
        return null;
    }

    @Override
    public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
        return new ArrayList<>(this.structureMap.values());
    }

    @Override
    public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
        return null;
    }

    @Override
    public ValueSet fetchValueSet(FhirContext fhirContext, String s) {
        return null;
    }

    @Override
    public CodeValidationResult validateCode(FhirContext fhirContext, String s, String s1, String s2, String s3) {
        return null;
    }

    @Override
    public CodeValidationResult validateCodeInValueSet(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
        return null;
    }

    @Override
    public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
        if (theClass.equals(StructureDefinition.class)) {
            final StructureDefinition definition = this.structureMap.get(theUri);
            if (definition != null) {
                return theClass.cast(definition);
            }
        }

        return null;
    }

    @Override
    public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {

        return this.structureMap.get(theUrl);
    }

    @Override
    public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
        return false;
    }

    @Override
    public LookupCodeResult lookupCode(FhirContext fhirContext, String s, String s1) {
        return null;
    }

    @Override
    public StructureDefinition generateSnapshot(StructureDefinition structureDefinition, String s, String s1) {
        return null;
    }

    private Map<String, StructureDefinition> loadProfiles(FhirContext ctx) {

        logger.info("Loading resource profiles");

        final Map<String, StructureDefinition> definitionMap = new HashMap<>();

        // Generate a validator to pull the base definitions from.
        final DefaultProfileValidationSupport defaultValidation = new DefaultProfileValidationSupport();

        final HapiWorkerContext hapiWorkerContext = new HapiWorkerContext(ctx, defaultValidation);

        final ProfileUtilities profileUtilities = new ProfileUtilities(hapiWorkerContext, new ArrayList<>(), null);

        final IParser parser = ctx.newJsonParser();

        ServiceLoaderHelpers.getLoaderStream(IProfileLoader.class)
                .map(profileLoader -> toStructureDefinition(parser, profileLoader.getPath()))
                .filter(Objects::nonNull)
                .map(diffStructure -> mergeDiff(ctx, defaultValidation, profileUtilities, diffStructure))
                .forEach(structure -> definitionMap.put(structure.getUrl(), structure));

        return definitionMap;
    }

    private StructureDefinition toStructureDefinition(IParser parser, String structurePath) {
        logger.info("Loading profile: {}", structurePath);
        try (InputStream stream = this.getClass().getClassLoader().getResourceAsStream(structurePath)) {
            if (stream == null) {
                throw new MissingResourceException("Cannot load structure definition", this.getClass().getName(), structurePath);
            }
            return parseStructureDefinition(parser, structurePath, stream);
        } catch (IOException e) {
            throw new IllegalStateException("For some reason, can't read.", e);
        }
    }

    private StructureDefinition parseStructureDefinition(IParser parser, String structurePath, InputStream stream) {
        try {
            return parser.parseResource(StructureDefinition.class, stream);
        } catch (DataFormatException e) {
            logger.error("Unable to parse profile: {}", structurePath, e);
            return null;
        }
    }

    private StructureDefinition mergeDiff(FhirContext ctx, DefaultProfileValidationSupport defaultValidation, ProfileUtilities utils, StructureDefinition diffStruct) {
        final StructureDefinition baseStructure = defaultValidation.fetchStructureDefinition(ctx, diffStruct.getBaseDefinition());
        if (baseStructure != null) {
            utils.generateSnapshot(baseStructure, diffStruct, "", "");
        }

        return diffStruct;
    }
}