001package org.hl7.fhir.common.hapi.validation.support;
002
003import ca.uhn.fhir.context.FhirContext;
004import ca.uhn.fhir.parser.IParser;
005import ca.uhn.fhir.parser.LenientErrorHandler;
006import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
007import ca.uhn.fhir.util.ClasspathUtil;
008import jakarta.annotation.Nonnull;
009import org.hl7.fhir.instance.model.api.IBaseResource;
010import org.hl7.fhir.utilities.TextFile;
011import org.hl7.fhir.utilities.npm.NpmPackage;
012
013import java.io.IOException;
014import java.io.InputStream;
015import java.nio.charset.StandardCharsets;
016import java.util.List;
017import java.util.Locale;
018
019/**
020 * This interceptor loads and parses FHIR NPM Conformance Packages, and makes the
021 * artifacts found within them available to the FHIR validator.
022 *
023 * @since 5.5.0
024 */
025public class NpmPackageValidationSupport extends PrePopulatedValidationSupport {
026
027        /**
028         * Constructor
029         */
030        public NpmPackageValidationSupport(@Nonnull FhirContext theFhirContext) {
031                super(theFhirContext);
032        }
033
034        /**
035         * Load an NPM package using a classpath specification, e.g. <code>/path/to/resource/my_package.tgz</code>. The
036         * classpath spec can optionally be prefixed with the string <code>classpath:</code>
037         *
038         * @throws InternalErrorException If the classpath file can't be found
039         */
040        public void loadPackageFromClasspath(String theClasspath) throws IOException {
041                try (InputStream is = ClasspathUtil.loadResourceAsStream(theClasspath)) {
042                        NpmPackage pkg = NpmPackage.fromPackage(is);
043                        if (pkg.getFolders().containsKey("package")) {
044                                loadResourcesFromPackage(pkg);
045                                loadBinariesFromPackage(pkg);
046                        }
047                }
048        }
049
050        private void loadResourcesFromPackage(NpmPackage thePackage) {
051                NpmPackage.NpmPackageFolder packageFolder = thePackage.getFolders().get("package");
052
053                for (String nextFile : packageFolder.listFiles()) {
054                        if (nextFile.toLowerCase(Locale.US).endsWith(".json")) {
055                                String input = new String(packageFolder.getContent().get(nextFile), StandardCharsets.UTF_8);
056                                IParser parser = getFhirContext().newJsonParser();
057                                parser.setParserErrorHandler(new LenientErrorHandler(false));
058                                IBaseResource resource = parser.parseResource(input);
059                                super.addResource(resource);
060                        }
061                }
062        }
063
064        private void loadBinariesFromPackage(NpmPackage thePackage) throws IOException {
065                List<String> binaries = thePackage.list("other");
066                for (String binaryName : binaries) {
067                        addBinary(TextFile.streamToBytes(thePackage.load("other", binaryName)), binaryName);
068                }
069        }
070}