Skip to content

Commit 09f1a6a

Browse files
committed
Implement multifile downloads
1 parent f5760dd commit 09f1a6a

4 files changed

Lines changed: 482 additions & 42 deletions

File tree

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,24 @@ Map<String, DatasetSeries> members = fusion.listDatasetMembers("my-catalog", "my
148148
```java
149149
Map<String, Distribution> distributions = fusion.listDistributions("my-catalog", "my-dataset", "my-series-member");
150150
```
151-
6. Download as a file
151+
6. List the files available in a distribution
152+
```java
153+
List<String> files = fusion.listDistributionFiles("my-dataset", "my-series-member", "csv", "my-catalog", 0);
154+
```
155+
7. Download all files in a distribution
152156
```java
153157
fusion.download("my-catalog", "my-dataset", "my-series-member", "csv", "/downloads/distributions");
154158
```
155-
7. Download as a stream
159+
8. Download specific files in a distribution
160+
```java
161+
List<String> fileNames = Arrays.asList("file1", "file2");
162+
fusion.download("my-catalog", "my-dataset", "my-series-member", "csv", "/downloads/distributions", fileNames);
163+
```
164+
9. Download as a stream (returns a Map of file names to InputStreams)
156165
```java
157-
InputStream is = fusion.downloadStream("my-catalog", "my-dataset", "my-series-member", "csv");
166+
Map<String, InputStream> streams = fusion.downloadStream("my-catalog", "my-dataset", "my-series-member", "csv");
167+
// Access individual file streams
168+
InputStream fileStream = streams.get("file1");
158169
```
159170

160171
#### Logging

src/main/java/io/github/jpmorganchase/fusion/Fusion.java

Lines changed: 212 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import static io.github.jpmorganchase.fusion.filter.DatasetFilter.filterDatasets;
44

5+
import com.google.gson.JsonArray;
6+
import com.google.gson.JsonParser;
57
import io.github.jpmorganchase.fusion.api.APIManager;
68
import io.github.jpmorganchase.fusion.api.FusionAPIManager;
79
import io.github.jpmorganchase.fusion.api.exception.APICallException;
@@ -24,13 +26,15 @@
2426
import io.github.jpmorganchase.fusion.parsing.DefaultGsonConfig;
2527
import io.github.jpmorganchase.fusion.parsing.GsonAPIResponseParser;
2628
import io.github.jpmorganchase.fusion.parsing.ParsingException;
27-
import java.io.IOException;
2829
import java.io.InputStream;
30+
import java.io.UnsupportedEncodingException;
31+
import java.net.URLEncoder;
2932
import java.nio.file.Files;
30-
import java.nio.file.InvalidPathException;
3133
import java.nio.file.Paths;
3234
import java.time.LocalDate;
3335
import java.time.format.DateTimeFormatter;
36+
import java.util.ArrayList;
37+
import java.util.Collections;
3438
import java.util.HashMap;
3539
import java.util.List;
3640
import java.util.Map;
@@ -281,6 +285,46 @@ public Map<String, Dataset> listDatasets(String catalogName, String contains, bo
281285
return filterDatasets(responseParser.parseDatasetResponse(json, catalogName), contains, idContains);
282286
}
283287

288+
/**
289+
* List distribution file names for a given dataset series and format.
290+
* <p>
291+
* This calls the Fusion API, parses the JSON response, and returns a list of
292+
* file names.
293+
* </p>
294+
*
295+
* @param dataset dataset identifier
296+
* @param series series member identifier
297+
* @param fileFormat file format / distribution identifier
298+
* @param catalog catalog identifier
299+
* @param maxResults maximum number of file names to return; if = 0, all are returned
300+
* @return list of file names
301+
* @throws APICallException if the call to the Fusion API fails
302+
* @throws ParsingException if the response cannot be parsed
303+
* @throws OAuthException if a token could not be retrieved for authentication
304+
*/
305+
public List<String> listDistributionFiles(
306+
String dataset, String series, String fileFormat, String catalog, int maxResults) {
307+
308+
String url = String.format(
309+
"%scatalogs/%s/datasets/%s/datasetseries/%s/distributions/%s/files",
310+
this.rootURL, catalog, dataset, series, fileFormat);
311+
312+
String json = this.api.callAPI(url);
313+
314+
if (json == null || json.trim().isEmpty()) {
315+
return Collections.emptyList();
316+
}
317+
318+
JsonArray arr = JsonParser.parseString(json).getAsJsonArray();
319+
List<String> files = new ArrayList<>();
320+
321+
for (int i = 0; i < arr.size() && (maxResults <= 0 || i < maxResults); i++) {
322+
files.add(arr.get(i).getAsString());
323+
}
324+
325+
return files;
326+
}
327+
284328
/**
285329
* Get a list of the datasets in the specified catalog
286330
*
@@ -498,7 +542,7 @@ public Map<String, Distribution> listDistributions(String dataset, String series
498542
* @throws OAuthException if a token could not be retrieved for authentication
499543
*/
500544
public void download(String catalogName, String dataset, String seriesMember, String distribution, String path) {
501-
download(catalogName, dataset, seriesMember, distribution, path, new HashMap<>());
545+
download(catalogName, dataset, seriesMember, distribution, path, (List<String>) null);
502546
}
503547

504548
/**
@@ -509,31 +553,82 @@ public void download(String catalogName, String dataset, String seriesMember, St
509553
* @param seriesMember a String representing the series member identifier.
510554
* @param distribution a String representing the distribution identifier, this is the file extension.
511555
* @param path the absolute file path where the file should be written.
512-
* @param headers http headers to be provided in the request. For headers with multiple instances, the value should be a csv list
556+
* @param fileNames a list of file names to download.
513557
* @throws APICallException if the call to the Fusion API fails
514558
* @throws FileDownloadException if there is an issue handling the response from Fusion API
515559
* @throws OAuthException if a token could not be retrieved for authentication
516560
*/
561+
public void download(
562+
String catalogName,
563+
String dataset,
564+
String seriesMember,
565+
String distribution,
566+
String path,
567+
List<String> fileNames) {
568+
download(catalogName, dataset, seriesMember, distribution, path, new HashMap<>(), fileNames);
569+
}
570+
517571
public void download(
518572
String catalogName,
519573
String dataset,
520574
String seriesMember,
521575
String distribution,
522576
String path,
523577
Map<String, String> headers) {
578+
download(catalogName, dataset, seriesMember, distribution, path, headers, null);
579+
}
580+
581+
public void download(
582+
String catalogName,
583+
String dataset,
584+
String seriesMember,
585+
String distribution,
586+
String path,
587+
Map<String, String> headers,
588+
List<String> fileNames) {
589+
590+
if (fileNames == null || fileNames.isEmpty()) {
591+
fileNames = listDistributionFiles(dataset, seriesMember, distribution, catalogName, 0);
592+
}
593+
594+
if (fileNames == null || fileNames.isEmpty()) {
595+
throw new FusionException(String.format(
596+
"No files found to download for catalog=%s, dataset=%s, series=%s, distribution=%s",
597+
catalogName, dataset, seriesMember, distribution));
598+
}
524599

525-
String url = String.format(
526-
"%scatalogs/%s/datasets/%s/datasetseries/%s/distributions/%s",
527-
this.rootURL, catalogName, dataset, seriesMember, distribution);
528600
try {
529601
Files.createDirectories(Paths.get(path));
530-
} catch (InvalidPathException | IOException e) {
531-
throw new FusionException(String.format("Unable to save to target path %s", path), e);
602+
} catch (Exception e) {
603+
throw new FusionException("Unable to save to " + path, e);
604+
}
605+
606+
for (String fileName : fileNames) {
607+
608+
// String safeFileName = (fileName.equalsIgnoreCase(distribution)
609+
// ? String.format("%s_%s_%s", catalogName, dataset, seriesMember)
610+
// : fileName)
611+
// .replaceAll("[^a-zA-Z0-9_.\\-]", "_");
612+
String safeFileName = fileName.replaceAll("[^a-zA-Z0-9_.\\-]", "_");
613+
String fullPath = path + "/" + safeFileName + "." + distribution;
614+
615+
String url = null;
616+
try {
617+
url = String.format(
618+
"%scatalogs/%s/datasets/%s/datasetseries/%s/distributions/%s?file=%s",
619+
this.rootURL,
620+
catalogName,
621+
dataset,
622+
seriesMember,
623+
distribution,
624+
URLEncoder.encode(fileName, "UTF-8"));
625+
626+
} catch (UnsupportedEncodingException e) {
627+
throw new RuntimeException(e);
628+
}
629+
630+
this.api.callAPIFileDownload(url, fullPath, catalogName, dataset, headers);
532631
}
533-
String fileName =
534-
String.format("%s_%s_%s", catalogName, dataset, seriesMember).replaceAll("[^a-zA-Z0-9_\\-]", "_");
535-
String filepath = String.format("%s/%s.%s", path, fileName, distribution);
536-
this.api.callAPIFileDownload(url, filepath, catalogName, dataset, headers);
537632
}
538633

539634
/**
@@ -548,7 +643,24 @@ public void download(
548643
* @throws OAuthException if a token could not be retrieved for authentication
549644
*/
550645
public void download(String catalogName, String dataset, String seriesMember, String distribution) {
551-
this.download(catalogName, dataset, seriesMember, distribution, getDefaultPath(), new HashMap<>());
646+
this.download(catalogName, dataset, seriesMember, distribution, getDefaultPath(), new HashMap<>(), null);
647+
}
648+
649+
/**
650+
* Download a single distribution to the local filesystem. By default, this will write to downloads folder.
651+
*
652+
* @param catalogName identifier of the catalog to be queried
653+
* @param dataset a String representing the dataset identifier to download.
654+
* @param seriesMember a String representing the series member identifier.
655+
* @param distribution a String representing the distribution identifier, this is the file extension.
656+
* @param fileNames a list of file names to download.
657+
* @throws APICallException if the call to the Fusion API fails
658+
* @throws FileDownloadException if there is an issue handling the response from Fusion API
659+
* @throws OAuthException if a token could not be retrieved for authentication
660+
*/
661+
public void download(
662+
String catalogName, String dataset, String seriesMember, String distribution, List<String> fileNames) {
663+
this.download(catalogName, dataset, seriesMember, distribution, getDefaultPath(), new HashMap<>(), fileNames);
552664
}
553665

554666
/**
@@ -565,7 +677,30 @@ public void download(String catalogName, String dataset, String seriesMember, St
565677
*/
566678
public void download(
567679
String catalogName, String dataset, String seriesMember, String distribution, Map<String, String> headers) {
568-
this.download(catalogName, dataset, seriesMember, distribution, getDefaultPath());
680+
download(catalogName, dataset, seriesMember, distribution, headers, null);
681+
}
682+
683+
/**
684+
* Download a single distribution to the local filesystem. By default, this will write to downloads folder.
685+
*
686+
* @param catalogName identifier of the catalog to be queried
687+
* @param dataset a String representing the dataset identifier to download.
688+
* @param seriesMember a String representing the series member identifier.
689+
* @param distribution a String representing the distribution identifier, this is the file extension.
690+
* @param headers http headers to be provided in the request. For headers with multiple instances, the value should be a csv list
691+
* @param fileNames a list of file names to download.
692+
* @throws APICallException if the call to the Fusion API fails
693+
* @throws FileDownloadException if there is an issue handling the response from Fusion API
694+
* @throws OAuthException if a token could not be retrieved for authentication
695+
*/
696+
public void download(
697+
String catalogName,
698+
String dataset,
699+
String seriesMember,
700+
String distribution,
701+
Map<String, String> headers,
702+
List<String> fileNames) {
703+
this.download(catalogName, dataset, seriesMember, distribution, getDefaultPath(), fileNames);
569704
}
570705

571706
/**
@@ -597,11 +732,9 @@ public void download(String catalogName, String dataset, List<String> seriesMemb
597732
* @throws FileDownloadException if there is an issue handling the response from Fusion API
598733
* @throws OAuthException if a token could not be retrieved for authentication
599734
*/
600-
public InputStream downloadStream(String catalogName, String dataset, String seriesMember, String distribution) {
601-
String url = String.format(
602-
"%scatalogs/%s/datasets/%s/datasetseries/%s/distributions/%s",
603-
this.rootURL, catalogName, dataset, seriesMember, distribution);
604-
return downloadStream(catalogName, dataset, seriesMember, distribution, new HashMap<>());
735+
public Map<String, InputStream> downloadStream(
736+
String catalogName, String dataset, String seriesMember, String distribution) {
737+
return downloadStream(catalogName, dataset, seriesMember, distribution, (List<String>) null);
605738
}
606739

607740
/**
@@ -613,17 +746,72 @@ public InputStream downloadStream(String catalogName, String dataset, String ser
613746
* @param dataset a String representing the dataset identifier to download.
614747
* @param seriesMember a String representing the series member identifier.
615748
* @param distribution a String representing the distribution identifier, this is the file extension.
616-
* @param headers http headers to be provided in the request. For headers with multiple instances, the value should be a csv list
749+
* @param fileNames a list of file names to download.
617750
* @throws APICallException if the call to the Fusion API fails
618751
* @throws FileDownloadException if there is an issue handling the response from Fusion API
619752
* @throws OAuthException if a token could not be retrieved for authentication
620753
*/
621-
public InputStream downloadStream(
622-
String catalogName, String dataset, String seriesMember, String distribution, Map<String, String> headers) {
754+
public Map<String, InputStream> downloadStream(
755+
String catalogName, String dataset, String seriesMember, String distribution, List<String> fileNames) {
623756
String url = String.format(
624757
"%scatalogs/%s/datasets/%s/datasetseries/%s/distributions/%s",
625758
this.rootURL, catalogName, dataset, seriesMember, distribution);
626-
return this.api.callAPIFileDownload(url, catalogName, dataset, headers);
759+
760+
return downloadStream(catalogName, dataset, seriesMember, distribution, new HashMap<>(), fileNames);
761+
}
762+
763+
public Map<String, InputStream> downloadStream(
764+
String catalogName, String dataset, String seriesMember, String distribution, Map<String, String> headers) {
765+
return downloadStream(catalogName, dataset, seriesMember, distribution, headers, null);
766+
}
767+
768+
public Map<String, InputStream> downloadStream(
769+
String catalogName,
770+
String dataset,
771+
String seriesMember,
772+
String distribution,
773+
Map<String, String> headers,
774+
List<String> fileNames) {
775+
776+
if (fileNames == null || fileNames.isEmpty()) {
777+
fileNames = listDistributionFiles(dataset, seriesMember, distribution, catalogName, 0);
778+
}
779+
780+
if (fileNames == null || fileNames.isEmpty()) {
781+
throw new FusionException(String.format(
782+
"No files found to download for catalog=%s, dataset=%s, series=%s, distribution=%s",
783+
catalogName, dataset, seriesMember, distribution));
784+
}
785+
786+
Map<String, InputStream> result = new HashMap<>();
787+
788+
for (String rawName : fileNames) {
789+
790+
if (rawName == null || rawName.trim().isEmpty()) {
791+
continue;
792+
}
793+
794+
String cleaned = rawName.replaceAll("[/\\\\]+$", "");
795+
796+
String url = null;
797+
try {
798+
url = String.format(
799+
"%scatalogs/%s/datasets/%s/datasetseries/%s/distributions/%s/files/operationType/download?file=%s",
800+
this.rootURL,
801+
catalogName,
802+
dataset,
803+
seriesMember,
804+
distribution,
805+
URLEncoder.encode(cleaned, "UTF-8"));
806+
} catch (UnsupportedEncodingException e) {
807+
throw new RuntimeException(e);
808+
}
809+
810+
InputStream stream = this.api.callAPIFileDownload(url, catalogName, dataset, headers);
811+
result.put(cleaned, stream);
812+
}
813+
814+
return result;
627815
}
628816

629817
/**

0 commit comments

Comments
 (0)