001package org.hl7.fhir.utilities.npm; 002 003import com.google.gson.JsonArray; 004import com.google.gson.JsonElement; 005import com.google.gson.JsonObject; 006import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 007import org.hl7.fhir.utilities.SimpleHTTPClient; 008import org.hl7.fhir.utilities.SimpleHTTPClient.HTTPResult; 009import org.hl7.fhir.utilities.TextFile; 010import org.hl7.fhir.utilities.Utilities; 011import org.hl7.fhir.utilities.VersionUtilities; 012import org.hl7.fhir.utilities.json.JSONUtil; 013import org.hl7.fhir.utilities.json.JsonTrackingParser; 014 015import java.io.ByteArrayInputStream; 016import java.io.FileNotFoundException; 017import java.io.IOException; 018import java.io.InputStream; 019import java.net.URL; 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.Date; 023import java.util.List; 024import java.util.Set; 025 026public class PackageClient { 027 028 public static final String PRIMARY_SERVER = "http://packages.fhir.org"; 029 public static final String SECONDARY_SERVER = "https://packages2.fhir.org/packages"; 030 031 private String address; 032 private String cacheFolder; 033 034 035 public PackageClient(String address) { 036 super(); 037 this.address = address; 038 try { 039 cacheFolder = Utilities.path(System.getProperty("user.home"), ".fhir", "package-client"); 040 Utilities.createDirectory(cacheFolder); 041 } catch (IOException e) { 042 } 043 } 044 045 public boolean exists(String id, String ver) throws IOException { 046 List<PackageInfo> vl = getVersions(id); 047 for (PackageInfo pi : vl) { 048 if (ver == null || ver.equals(pi.getVersion())) { 049 return true; 050 } 051 } 052 return false; 053 } 054 055 public InputStream fetch(String id, String ver) throws IOException { 056 return fetchCached(Utilities.pathURL(address, id, ver)); 057 } 058 059 public InputStream fetch(PackageInfo info) throws IOException { 060 return fetchCached(Utilities.pathURL(address, info.getId(), info.getVersion())); 061 } 062 063 public InputStream fetchNpm(String id, String ver) throws IOException { 064 return fetchCached(Utilities.pathURL(address, id, "-", id+"-"+ver+".tgz")); 065 } 066 067 public InputStream fetchCached(String url) throws IOException, FileNotFoundException { 068 return fetchUrl(url, null); 069 } 070 071 protected String fn(String url) { 072 String[] p = url.split("\\/"); 073 return p[2]+"-"+p[p.length-2]+"-"+p[p.length-1]+".tgz"; 074 } 075 076 public List<PackageInfo> getVersions(String id) throws IOException { 077 List<PackageInfo> res = new ArrayList<>(); 078 JsonObject json; 079 try { 080 json = fetchJson(Utilities.pathURL(address, id)); 081 JsonObject versions = json.getAsJsonObject("versions"); 082 if (versions != null) { 083 for (String v : sorted(versions.keySet())) { 084 JsonObject obj = versions.getAsJsonObject(v); 085 res.add(new PackageInfo(JSONUtil.str(obj, "name"), 086 JSONUtil.str(obj, "version"), 087 JSONUtil.str(obj, "FhirVersion"), 088 JSONUtil.str(obj, "description"), 089 JSONUtil.str(obj, "url"), 090 JSONUtil.str(obj, "canonical"), 091 address)); 092 } 093 } 094 } catch (FileNotFoundException e) { 095 } 096 return res; 097 } 098 099 private List<String> sorted(Set<String> keys) { 100 List<String> res = new ArrayList<>(); 101 res.addAll(keys); 102 Collections.sort(res); 103 return res; 104 } 105 106 public List<PackageInfo> search(String name, String canonical, String fhirVersion, boolean preRelease) throws IOException { 107 CommaSeparatedStringBuilder params = new CommaSeparatedStringBuilder("&"); 108 if (!Utilities.noString(name)) { 109 params.append("name="+name); 110 } 111 if (!Utilities.noString(canonical)) { 112 params.append("canonical="+canonical); 113 } 114 if (!Utilities.noString(fhirVersion)) { 115 params.append("fhirversion="+fhirVersion); 116 } 117 if (preRelease) { 118 params.append("prerelease="+preRelease); 119 } 120 List<PackageInfo> res = new ArrayList<>(); 121 try { 122 JsonArray json = fetchJsonArray(Utilities.pathURL(address, "catalog?")+params.toString()); 123 for (JsonElement e : json) { 124 JsonObject obj = (JsonObject) e; 125 res.add(new PackageInfo(JSONUtil.str(obj, "Name", "name"), 126 JSONUtil.str(obj, "Version", "version"), 127 JSONUtil.str(obj, "FhirVersion", "fhirVersion"), 128 JSONUtil.str(obj, "Description", "description"), 129 JSONUtil.str(obj, "url"), 130 JSONUtil.str(obj, "canonical"), 131 address)); 132 } 133 } catch (IOException e1) { 134 } 135 return res; 136 } 137 138 public Date getNewPackages(Date lastCalled, List<PackageInfo> updates) { 139 return null; 140 } 141 142 private InputStream fetchUrl(String source, String accept) throws IOException { 143 SimpleHTTPClient http = new SimpleHTTPClient(); 144 HTTPResult res = http.get(source, accept); 145 res.checkThrowException(); 146 return new ByteArrayInputStream(res.getContent()); 147 } 148 149 private JsonObject fetchJson(String source) throws IOException { 150 String src = TextFile.streamToString(fetchUrl(source, "application/json")); 151 //System.out.println(src); 152 return (JsonObject) new com.google.gson.JsonParser().parse(src); 153 } 154 155 private JsonArray fetchJsonArray(String source) throws IOException { 156 String src = TextFile.streamToString(fetchUrl(source, "application/json")); 157 //System.out.println(src); 158 return (JsonArray) new com.google.gson.JsonParser().parse(src); 159 } 160 161 public String url(String id, String v) { 162 return Utilities.pathURL(address, id, v); 163 } 164 165 public String getLatestVersion(String id) throws IOException { 166 List<PackageInfo> list = getVersions(id); 167 if (list.isEmpty()) { 168 throw new IOException("Package not found: "+id); 169 } else { 170 String v = list.get(0).getVersion(); 171 for (PackageInfo p : list) { 172 if (VersionUtilities.isThisOrLater(v, p.getVersion())) { 173 v = p.getVersion(); 174 } 175 } 176 return v; 177 } 178 } 179 180 public String getLatestVersion(String id, String majMinVersion) throws IOException { 181 List<PackageInfo> list = getVersions(id); 182 if (list.isEmpty()) { 183 throw new IOException("Package not found: "+id); 184 } else { 185 String v = majMinVersion; 186 for (PackageInfo p : list) { 187 if (VersionUtilities.isMajMinOrLaterPatch(v, p.getVersion())) { 188 v = p.getVersion(); 189 } 190 } 191 return v; 192 } 193 } 194 195 196 public List<PackageInfo> listFromRegistry(String name, String canonical, String fhirVersion) throws IOException { 197 List<PackageInfo> result = new ArrayList<>(); 198 JsonObject packages = JsonTrackingParser.fetchJson("https://raw.githubusercontent.com/FHIR/ig-registry/master/fhir-ig-list.json?nocache=" + System.currentTimeMillis()); 199 for (JsonObject o : JSONUtil.objects(packages, "guides")) { 200 if (o.has("canonical")) { 201 String id = JSONUtil.str(o, "npm-name"); 202 String pname = JSONUtil.str(o, "name"); 203 String pcanonical = JSONUtil.str(o, "canonical"); 204 String description = JSONUtil.str(o, "description"); 205 boolean ok = true; 206 if (ok && !Utilities.noString(name)) { 207 ok = (pname != null && pname.contains(name)) || (description != null && description.contains(name)) || (id != null && id.contains(name)); 208 } 209 if (ok && !Utilities.noString(canonical)) { 210 ok = pcanonical.contains(canonical); 211 } 212 String version = null; 213 String fVersion = null; 214 String url = null; 215 216 if (ok) { 217 // if we can find something... 218 for (JsonObject e : JSONUtil.objects(o, "editions")) { 219 if (fhirVersion == null || fhirVersion.equals(JSONUtil.str(e, "fhir-version"))) { 220 String v = JSONUtil.str(e, "ig-version"); 221 if (version == null || VersionUtilities.isThisOrLater(version, v)) { 222 version = v; 223 fVersion = e.getAsJsonArray("fhir-version").get(0).getAsString(); 224 url = JSONUtil.str(e, "url"); 225 } 226 } 227 } 228 } 229 if (version != null) { 230 result.add(new PackageInfo(id, version, fVersion, description, url, pcanonical, address)); 231 } 232 } 233 } 234 return result; 235 } 236 237}