• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Bynder / bynder-java-sdk / 16054241626

18 Mar 2025 10:16PM UTC coverage: 39.867%. Remained the same
16054241626

Pull #135

github

web-flow
update sdk to version 2.2.27 (#134)
Pull Request #135: Log poll import

0 of 3 new or added lines in 1 file covered. (0.0%)

50 existing lines in 2 files now uncovered.

781 of 1959 relevant lines covered (39.87%)

0.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

5.06
/src/main/java/com/bynder/sdk/service/upload/FileUploader.java
1
/*
2
 * Copyright (c) 2017 Bynder B.V. All rights reserved.
3
 * <p>
4
 * Licensed under the MIT License. See LICENSE file in the project root for full license
5
 * information.
6
 */
7
package com.bynder.sdk.service.upload;
8

9
import com.bynder.sdk.api.BynderApi;
10
import com.bynder.sdk.exception.BynderUploadException;
11
import com.bynder.sdk.model.upload.*;
12
import com.bynder.sdk.query.decoder.QueryDecoder;
13
import com.bynder.sdk.query.upload.*;
14
import com.bynder.sdk.service.amazons3.AmazonS3Service;
15
import com.bynder.sdk.util.RXUtils;
16
import io.reactivex.Completable;
17
import io.reactivex.Observable;
18
import io.reactivex.Single;
19

20
import java.io.IOException;
21
import java.nio.file.Files;
22
import java.nio.file.Paths;
23
import java.util.Map;
24
import java.util.concurrent.TimeUnit;
25

26
/**
27
 * Class used to upload files to Bynder.
28
 */
29
public class FileUploader {
30

31
    private static final int MAX_CHUNK_SIZE = 1024 * 1024 * 5;
32

33
    /**
34
     * Max polling iterations to wait for the file to be converted.
35
     */
36
    private static final int MAX_POLLING_ITERATIONS = 120;
37

38
    /**
39
     * Idle time between polling iterations.
40
     */
41
    private static final int POLLING_IDLE_TIME = 2000;
42

43
    /**
44
     * Instance of {@link BynderApi} which handles the HTTP communication.
45
     */
46
    private final BynderApi bynderApi;
47

48
    /**
49
     * Instance of {@link QueryDecoder} to decode query objects into API parameters.
50
     */
51
    private final QueryDecoder queryDecoder;
52

53
    /**
54
     * Creates a new instance of the class.
55
     *
56
     * @param bynderApi    Instance to handle the HTTP communication with the Bynder API.
57
     * @param queryDecoder Query decoder.
58
     */
59
    public FileUploader(final BynderApi bynderApi, final QueryDecoder queryDecoder) {
1✔
60
        this.bynderApi = bynderApi;
1✔
61
        this.queryDecoder = queryDecoder;
1✔
62
    }
1✔
63

64
    /**
65
     * Uploads a file with the information specified in the query parameter.
66
     *
67
     * @param uploadQuery Upload query with the information to upload the file.
68
     * @return {@link Observable} with the {@link SaveMediaResponse} information.
69
     */
70
    public Single<SaveMediaResponse> uploadFile(final UploadQuery uploadQuery) {
UNCOV
71
        return getClosestS3Endpoint().flatMap(awsBucket -> {
×
UNCOV
72
            String filename = uploadQuery.getFilename();
×
UNCOV
73
            AmazonS3Service amazonS3Service = AmazonS3Service.Builder.create(awsBucket);
×
UNCOV
74
            return getUploadInformation(new RequestUploadQuery(filename))
×
UNCOV
75
                    .flatMap(uploadRequest -> uploadChunk(
×
76
                            amazonS3Service,
77
                            uploadRequest,
78
                            uploadQuery,
79
                            filename
80
                    ).count().flatMap(chunkCount -> finaliseUpload(new FinaliseUploadQuery(
×
UNCOV
81
                            uploadRequest.getS3File().getUploadId(),
×
UNCOV
82
                            uploadRequest.getS3File().getTargetId(),
×
UNCOV
83
                            uploadRequest.getS3Filename(),
×
84
                            chunkCount
85
                    ))));
86
        }).flatMap(importId ->
×
87
                pollProcessing(importId).andThen(saveUploadedMedia(importId, uploadQuery))
×
88
        );
89
    }
90

91
    public Single<UploadAdditionalMediaResponse> uploadAdditionalFile(final UploadQuery uploadQuery) {
92
        return getClosestS3Endpoint().flatMap(awsBucket -> {
×
UNCOV
93
            String filename = uploadQuery.getFilename();
×
UNCOV
94
            AmazonS3Service amazonS3Service = AmazonS3Service.Builder.create(awsBucket);
×
UNCOV
95
            return getUploadInformation(new RequestUploadQuery(filename))
×
UNCOV
96
                    .flatMap(uploadRequest -> uploadChunk(
×
97
                            amazonS3Service,
98
                            uploadRequest,
99
                            uploadQuery,
100
                            filename
101
                    ).count().flatMap(chunkCount -> finaliseUploadAdditional(new FinaliseUploadAdditionalQuery(
×
UNCOV
102
                            uploadRequest.getS3File().getUploadId(),
×
UNCOV
103
                            uploadRequest.getS3File().getTargetId(),
×
UNCOV
104
                            uploadRequest.getS3Filename(),
×
105
                            chunkCount
106
                    ), uploadQuery.getMediaId())));
×
107
        });
108
    }
109

110
    /**
111
     * Uploads a file with the information specified in the query parameter
112
     * while providing information on the progress of the upload via the Observable returned.
113
     *
114
     * @param uploadQuery Upload query with the information to upload the file.
115
     * @return {@link Observable} with the {@link UploadProgress} information.
116
     */
117
    public Observable<UploadProgress> uploadFileWithProgress(final UploadQuery uploadQuery) {
UNCOV
118
        String filename = uploadQuery.getFilename();
×
UNCOV
119
        return getClosestS3Endpoint().flatMapObservable(awsBucket -> {
×
UNCOV
120
            AmazonS3Service amazonS3Service = AmazonS3Service.Builder.create(awsBucket);
×
UNCOV
121
            return getUploadInformation(new RequestUploadQuery(filename))
×
UNCOV
122
                    .flatMapObservable(uploadRequest -> uploadChunk(
×
123
                            amazonS3Service,
124
                            uploadRequest,
125
                            uploadQuery,
126
                            filename
127
                    ));
128
        });
129
    }
130

131
    private Observable<UploadProgress> uploadChunk(
132
            AmazonS3Service amazonS3Service,
133
            UploadRequest uploadRequest,
134
            UploadQuery uploadQuery,
135
            String filename
136
    ) throws IOException {
UNCOV
137
        long fileSize = Files.size(Paths.get(uploadQuery.getFilepath()));
×
UNCOV
138
        UploadProgress uploadProgress = new UploadProgress(fileSize);
×
UNCOV
139
        return RXUtils.mapWithIndex(
×
UNCOV
140
                RXUtils.readFileChunks(uploadQuery.getFilepath(), MAX_CHUNK_SIZE),
×
141
                1
142
        ).flatMapSingle(chunk -> amazonS3Service.uploadPartToAmazon(
×
143
                chunk,
144
                filename,
145
                (int) ((fileSize - 1) / MAX_CHUNK_SIZE + 1),
UNCOV
146
                uploadRequest.getMultipartParams()
×
147
        ).andThen(registerChunk(new RegisterChunkQuery(
×
UNCOV
148
                chunk.getIndex(),
×
UNCOV
149
                uploadRequest.getS3File().getUploadId(),
×
UNCOV
150
                uploadRequest.getS3File().getTargetId(),
×
151
                String.format(
×
152
                        "%s/p%s",
153
                        uploadRequest.getS3Filename(),
×
154
                        chunk.getIndex()
×
155
                )
156
        ))).toSingle(() -> uploadProgress.addProgress(chunk.getValue().length)));
×
157
    }
158

159
    /**
160
     * Check {@link BynderApi#getPollStatus} for more information.
161
     */
162
    private Completable pollProcessing(final String importId) {
UNCOV
163
        return getPollStatus(new PollStatusQuery(importId.split(",")))
×
UNCOV
164
                .delay(POLLING_IDLE_TIME, TimeUnit.MILLISECONDS)
×
UNCOV
165
                .repeat()
×
UNCOV
166
                .take(MAX_POLLING_ITERATIONS)
×
UNCOV
167
                .takeUntil(pollStatus -> pollStatus.processingDone(importId) || pollStatus.processingFailed(importId))
×
NEW
168
                .lastElement()
×
169
                .toSingle()
×
170
                .flatMapCompletable(pollStatus -> {
×
171
                    if (pollStatus.processingFailed(importId)) {
×
172
                        return Completable.error(new BynderUploadException("Processing media failed."));
×
173
                    }
174
                    return Completable.complete();
×
175
                });
176
    }
177

178
    /**
179
     * Calls {@link FileUploader#saveMedia(SaveMediaQuery)} to save the completely uploaded file in
180
     * Bynder.
181
     *
182
     * @param uploadQuery Upload query with the information to upload the file.
183
     * @return {@link Single} with the {@link SaveMediaResponse} information.
184
     */
185
    private Single<SaveMediaResponse> saveUploadedMedia(final String importId, final UploadQuery uploadQuery) {
UNCOV
186
        SaveMediaQuery saveMediaQuery = new SaveMediaQuery(importId)
×
UNCOV
187
                .setAudit(uploadQuery.isAudit())
×
UNCOV
188
                .setMetaproperties(uploadQuery.getMetaproperties());
×
189

UNCOV
190
        if (uploadQuery.getMediaId() == null) {
×
191
            // A new asset will be created for the uploaded file.
192
            return saveMedia(saveMediaQuery
×
193
                    .setBrandId(uploadQuery.getBrandId())
×
194
                    .setName(uploadQuery.getAssetName())
×
UNCOV
195
                    .setTags(uploadQuery.getTags())
×
196
            );
197
        } else {
198
            // The uploaded file will be attached to an existing asset.
199
            return saveMedia(saveMediaQuery
×
200
                    .setMediaId(uploadQuery.getMediaId())
×
201
            );
202
        }
203
    }
204

205
    /**
206
     * Check {@link BynderApi#getClosestS3Endpoint()} for more information.
207
     */
208
    private Single<String> getClosestS3Endpoint() {
UNCOV
209
        return bynderApi.getClosestS3Endpoint().singleOrError().map(RXUtils::getResponseBody);
×
210
    }
211

212
    /**
213
     * Check {@link BynderApi#getUploadInformation(Map)} for more information.
214
     */
215
    private Single<UploadRequest> getUploadInformation(final RequestUploadQuery requestUploadQuery) {
UNCOV
216
        Map<String, String> params = queryDecoder.decode(requestUploadQuery);
×
UNCOV
217
        return bynderApi.getUploadInformation(params).singleOrError().map(RXUtils::getResponseBody);
×
218
    }
219

220
    /**
221
     * Check {@link BynderApi#registerChunk(Map)} for more information.
222
     */
223
    private Completable registerChunk(final RegisterChunkQuery registerChunkQuery) {
UNCOV
224
        Map<String, String> params = queryDecoder.decode(registerChunkQuery);
×
UNCOV
225
        return bynderApi.registerChunk(params).ignoreElements();
×
226
    }
227

228
    /**
229
     * Check {@link BynderApi#finaliseUpload(Map)} for more information.
230
     */
231
    private Single<String> finaliseUpload(final FinaliseUploadQuery finaliseUploadQuery) {
UNCOV
232
        Map<String, String> params = queryDecoder.decode(finaliseUploadQuery);
×
UNCOV
233
        return bynderApi.finaliseUpload(params).singleOrError().map(RXUtils::getResponseBody)
×
UNCOV
234
                .map(FinaliseResponse::getImportId);
×
235
    }
236

237
    private Single<UploadAdditionalMediaResponse> finaliseUploadAdditional(final FinaliseUploadAdditionalQuery finaliseUploadQuery, String mediaId) {
238
        Map<String, String> params = queryDecoder.decode(finaliseUploadQuery);
×
239
        return bynderApi.finaliseUploadAdditional(mediaId, params).singleOrError().map(RXUtils::getResponseBody);
×
240
    }
241

242
    /**
243
     * Check {@link BynderApi#getPollStatus(Map)} for more information.
244
     */
245
    private Single<PollStatus> getPollStatus(final PollStatusQuery pollStatusQuery) {
UNCOV
246
        Map<String, String> params = queryDecoder.decode(pollStatusQuery);
×
UNCOV
247
        return bynderApi.getPollStatus(params).singleOrError().map(RXUtils::getResponseBody);
×
248
    }
249

250
    /**
251
     * Check {@link BynderApi#saveMedia(Map)} for more information.
252
     */
253
    private Single<SaveMediaResponse> saveMedia(final SaveMediaQuery saveMediaQuery) {
NEW
254
        Map<String, String> params = queryDecoder.decode(saveMediaQuery);
×
NEW
255
        return bynderApi.saveMedia(params).singleOrError().map(RXUtils::getResponseBody);
×
256
    }
257
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc