2222import  com .google .common .base .Preconditions ;
2323import  com .google .common .base .Strings ;
2424import  com .google .common .collect .ImmutableMap ;
25- import  java .io .BufferedInputStream ;
2625import  java .io .IOException ;
2726import  java .io .InputStream ;
2827import  java .util .Collection ;
3938import  org .apache .commons .lang3 .tuple .Pair ;
4039import  org .datatransferproject .api .launcher .Monitor ;
4140import  org .datatransferproject .spi .api .transport .JobFileStream ;
42- import  org .datatransferproject .spi .api .transport .RemoteFileStreamer ;
43- import  org .datatransferproject .spi .api .transport .UrlGetStreamer ;
4441import  org .datatransferproject .spi .cloud .storage .TemporaryPerJobDataStore ;
4542import  org .datatransferproject .spi .transfer .idempotentexecutor .IdempotentImportExecutor ;
4643import  org .datatransferproject .spi .transfer .provider .ImportResult ;
@@ -79,13 +76,17 @@ public class MicrosoftMediaImporter
7976
8077  private  static  final  String  UPLOAD_PARAMS  = "?@microsoft.graph.conflictBehavior=rename" ;
8178
82-   public  MicrosoftMediaImporter (String  baseUrl , OkHttpClient  client , ObjectMapper  objectMapper ,
83-       TemporaryPerJobDataStore  jobStore , Monitor  monitor ,
79+   public  MicrosoftMediaImporter (
80+       String  baseUrl ,
81+       OkHttpClient  client ,
82+       ObjectMapper  objectMapper ,
83+       TemporaryPerJobDataStore  jobStore ,
84+       Monitor  monitor ,
8485      MicrosoftCredentialFactory  credentialFactory ,
8586      JobFileStream  jobFileStream ) {
8687
8788    // NOTE: "special/photos" is a specific folder in One Drive that corresponds to items that 
88-     // should appear in https://photos.onedrive.com/, for more information see:    
89+     // should appear in https://photos.onedrive.com/, for more information see: 
8990    // https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/drive_get_specialfolder?#special-folder-names 
9091    createFolderUrl  = baseUrl  + "/v1.0/me/drive/special/photos/children" ;
9192    albumlessMediaUrlTemplate  =
@@ -105,8 +106,12 @@ public MicrosoftMediaImporter(String baseUrl, OkHttpClient client, ObjectMapper
105106  }
106107
107108  @ Override 
108-   public  ImportResult  importItem (UUID  jobId , IdempotentImportExecutor  idempotentImportExecutor ,
109-       TokensAndUrlAuthData  authData , MediaContainerResource  resource ) throws  Exception  {
109+   public  ImportResult  importItem (
110+       UUID  jobId ,
111+       IdempotentImportExecutor  idempotentImportExecutor ,
112+       TokensAndUrlAuthData  authData ,
113+       MediaContainerResource  resource )
114+       throws  Exception  {
110115    // Ensure credential is populated 
111116    getOrCreateCredential (authData );
112117
@@ -134,19 +139,17 @@ public ImportResult importItem(UUID jobId, IdempotentImportExecutor idempotentIm
134139   * Logs brief debugging message describing current state of job given `resource`. 
135140   * 
136141   * @param format a printf-style formatter that exactly one parameter: the string of the job's 
137-    *   current status. 
142+    *      current status. 
138143   */ 
139-   private  void  logDebugJobStatus (
140-       String  format ,
141-       UUID  jobId ,
142-       MediaContainerResource  resource ) {
143-     String  statusMessage  = String .format (
144-         "%s: Importing %s albums, %s photos, and %s videos" ,
145-         jobId ,
146-         resource .getAlbums ().size (),
147-         resource .getPhotos ().size (),
148-         resource .getVideos ().size ());
149-     monitor .debug (() ->  String .format (format , statusMessage ));
144+   private  void  logDebugJobStatus (String  format , UUID  jobId , MediaContainerResource  resource ) {
145+     String  statusMessage  =
146+         String .format (
147+             "%s: Importing %s albums, %s photos, and %s videos" ,
148+             jobId ,
149+             resource .getAlbums ().size (),
150+             resource .getPhotos ().size (),
151+             resource .getVideos ().size ());
152+     monitor .debug (() -> String .format (format , statusMessage ));
150153  }
151154
152155  @ SuppressWarnings ("unchecked" )
@@ -165,8 +168,9 @@ private String createOneDriveFolder(MediaAlbum album)
165168
166169    Request .Builder  requestBuilder  = new  Request .Builder ().url (createFolderUrl );
167170    requestBuilder .header ("Authorization" , "Bearer "  + credential .getAccessToken ());
168-     requestBuilder .post (RequestBody .create (
169-         MediaType .parse ("application/json" ), objectMapper .writeValueAsString (rawFolder )));
171+     requestBuilder .post (
172+         RequestBody .create (
173+             MediaType .parse ("application/json" ), objectMapper .writeValueAsString (rawFolder )));
170174    try  (Response  response  = client .newCall (requestBuilder .build ()).execute ()) {
171175      int  code  = response .code ();
172176      ResponseBody  body  = response .body ();
@@ -182,12 +186,18 @@ private String createOneDriveFolder(MediaAlbum album)
182186      }
183187
184188      if  (code  == 403  && response .message ().contains ("Access Denied" )) {
185-         throw  new  PermissionDeniedException ("User access to microsoft onedrive was denied" ,
189+         throw  new  PermissionDeniedException (
190+             "User access to microsoft onedrive was denied" ,
186191            new  IOException (
187192                String .format ("Got error code %d  with message: %s" , code , response .message ())));
188193      } else  if  (code  < 200  || code  > 299 ) {
189-         throw  new  IOException ("Got error code: "  + code  + " message: "  + response .message ()
190-             + " body: "  + response .body ().string ());
194+         throw  new  IOException (
195+             "Got error code: " 
196+                 + code 
197+                 + " message: " 
198+                 + response .message ()
199+                 + " body: " 
200+                 + response .body ().string ());
191201      } else  if  (body  == null ) {
192202        throw  new  IOException ("Got null body" );
193203      }
@@ -203,10 +213,12 @@ private String createOneDriveFolder(MediaAlbum album)
203213  private  void  executeIdempotentImport (
204214      UUID  jobId ,
205215      IdempotentImportExecutor  idempotentImportExecutor ,
206-       Collection <? extends  DownloadableFile > downloadableFiles ) throws  Exception  {
216+       Collection <? extends  DownloadableFile > downloadableFiles )
217+       throws  Exception  {
207218    for  (DownloadableFile  downloadableFile  : downloadableFiles ) {
208219      idempotentImportExecutor .executeAndSwallowIOExceptions (
209-           downloadableFile .getIdempotentId (), downloadableFile .getName (),
220+           downloadableFile .getIdempotentId (),
221+           downloadableFile .getName (),
210222          () -> importDownloadableItem (downloadableFile , jobId , idempotentImportExecutor ));
211223    }
212224  }
@@ -216,9 +228,10 @@ private String importDownloadableItem(
216228      throws  Exception  {
217229    final  long  totalFileSize  = discardForLength (jobFileStream .streamFile (item , jobId , jobStore ));
218230    if  (totalFileSize  <= 0 ) {
219-       throw  new  IOException (String .format (
220-           "jobid %s hit empty unexpectedly empty (bytes=%d) download for file %s" ,
221-           jobId , totalFileSize , item .getFetchableUrl ()));
231+       throw  new  IOException (
232+           String .format (
233+               "jobid %s hit empty unexpectedly empty (bytes=%d) download for file %s" ,
234+               jobId , totalFileSize , item .getFetchableUrl ()));
222235    }
223236    InputStream  fileStream  = jobFileStream .streamFile (item , jobId , jobStore );
224237
@@ -242,7 +255,8 @@ private String importDownloadableItem(
242255
243256  /** Depletes input stream, uploading a chunk of the stream at a time. */ 
244257  private  Response  uploadStreamInChunks (
245-       long  totalFileSize , String  itemUploadUrl , String  itemMimeType , InputStream  inputStream ) throws  IOException , DestinationMemoryFullException  {
258+       long  totalFileSize , String  itemUploadUrl , String  itemMimeType , InputStream  inputStream )
259+       throws  IOException , DestinationMemoryFullException  {
246260    Response  lastChunkResponse  = null ;
247261    StreamChunker  streamChunker  = new  StreamChunker (MICROSOFT_UPLOAD_CHUNK_BYTE_SIZE , inputStream );
248262    Optional <DataChunk > nextChunk ;
@@ -251,8 +265,7 @@ private Response uploadStreamInChunks(
251265      if  (nextChunk .isEmpty ()) {
252266        break ;
253267      }
254-       lastChunkResponse  =
255-           uploadChunk (nextChunk .get (), itemUploadUrl , totalFileSize , itemMimeType );
268+       lastChunkResponse  = uploadChunk (nextChunk .get (), itemUploadUrl , totalFileSize , itemMimeType );
256269    }
257270    return  lastChunkResponse ;
258271  }
@@ -286,37 +299,48 @@ private Pair<Request, Response> tryWithCreds(Request.Builder requestBuilder) thr
286299  private  String  createUploadSession (
287300      DownloadableFile  item , IdempotentImportExecutor  idempotentImportExecutor )
288301      throws  IOException , CopyExceptionWithFailureReason  {
289-     Request .Builder  createSessionRequestBuilder  = buildCreateUploadSessionPath (item , idempotentImportExecutor );
302+     Request .Builder  createSessionRequestBuilder  =
303+         buildCreateUploadSessionPath (item , idempotentImportExecutor );
290304
291305    // Auth headers 
292306    createSessionRequestBuilder .header ("Authorization" , "Bearer "  + credential .getAccessToken ());
293307    createSessionRequestBuilder .header ("Content-Type" , "application/json" );
294308
295309    // Post request with empty body. If you don't include an empty body, you'll have problems 
296-     createSessionRequestBuilder .post (RequestBody .create (
297-         MediaType .parse ("application/json" ), objectMapper .writeValueAsString (ImmutableMap .of ())));
310+     createSessionRequestBuilder .post (
311+         RequestBody .create (
312+             MediaType .parse ("application/json" ),
313+             objectMapper .writeValueAsString (ImmutableMap .of ())));
298314
299315    // Make the call, we should get an upload url for item data in a 200 response 
300316    Pair <Request , Response > reqResp  = tryWithCreds (createSessionRequestBuilder );
301317    Response  response  = reqResp .getRight ();
302318    int  code  = response .code ();
303319    if  (code  == 403  && response .message ().contains ("Access Denied" )) {
304-       throw  new  PermissionDeniedException ("User access to Microsoft One Drive was denied" ,
320+       throw  new  PermissionDeniedException (
321+           "User access to Microsoft One Drive was denied" ,
305322          new  IOException (
306323              String .format ("Got error code %d  with message: %s" , code , response .message ())));
307324    } else  if  (code  == 507  && response .message ().contains ("Insufficient Storage" )) {
308-       throw  new  DestinationMemoryFullException ("Microsoft destination storage limit reached" ,
325+       throw  new  DestinationMemoryFullException (
326+           "Microsoft destination storage limit reached" ,
309327          new  IOException (
310328              String .format ("Got error code %d  with message: %s" , code , response .message ())));
311329    } else  if  (code  < 200  || code  > 299 ) {
312-       throw  new  IOException (String .format ("Got error code: %s\n " 
313-               + "message: %s\n " 
314-               + "body: %s\n " 
315-               + "request url: %s\n " 
316-               + "bearer token: %s\n " 
317-               + " item: %s\n " , // For debugging 404s on upload 
318-           code , response .message (), response .body ().string (), reqResp .getLeft ().url (),
319-           credential .getAccessToken (), item ));
330+       throw  new  IOException (
331+           String .format (
332+               "Got error code: %s\n " 
333+                   + "message: %s\n " 
334+                   + "body: %s\n " 
335+                   + "request url: %s\n " 
336+                   + "bearer token: %s\n " 
337+                   + " item: %s\n " , // For debugging 404s on upload 
338+               code ,
339+               response .message (),
340+               response .body ().string (),
341+               reqResp .getLeft ().url (),
342+               credential .getAccessToken (),
343+               item ));
320344    } else  if  (code  != 200 ) {
321345      monitor .info (() -> String .format ("Got an unexpected non-200, non-error response code" ));
322346    }
@@ -335,13 +359,12 @@ private String createUploadSession(
335359  /** 
336360   * Forms the URL to create an upload session. 
337361   * 
338-    * Creates an upload session path for one of two cases: 
339-    * - 1) POST to  /me/drive/items/{folder_id}:/{file_name}:/createUploadSession 
340-    * - 2) GET {uploadurl} from  /me/drive/items/root:/photos-video/{file_name}:/createUploadSession 
362+    * <p> Creates an upload session path for one of two cases: - 1) POST to  
363+    * /me/drive/items/{folder_id}:/{file_name}:/createUploadSession - 2) GET {uploadurl} from  
364+    * /me/drive/items/root:/photos-video/{file_name}:/createUploadSession 
341365   */ 
342366  private  Request .Builder  buildCreateUploadSessionPath (
343-       DownloadableFile  item ,
344-       IdempotentImportExecutor  idempotentImportExecutor ) {
367+       DownloadableFile  item , IdempotentImportExecutor  idempotentImportExecutor ) {
345368    String  createSessionUrl ;
346369    if  (Strings .isNullOrEmpty (item .getFolderId ())) {
347370      createSessionUrl  = String .format (albumlessMediaUrlTemplate , item .getName (), UPLOAD_PARAMS );
@@ -360,8 +383,9 @@ private Request.Builder buildCreateUploadSessionPath(
360383  // Content-Length: {chunk size in bytes} 
361384  // Content-Range: bytes {begin}-{end}/{total size} 
362385  // body={bytes} 
363-   private  Response  uploadChunk (DataChunk  chunk , String  photoUploadUrl , long  totalFileSize ,
364-       String  mediaType ) throws  IOException , DestinationMemoryFullException  {
386+   private  Response  uploadChunk (
387+       DataChunk  chunk , String  photoUploadUrl , long  totalFileSize , String  mediaType )
388+       throws  IOException , DestinationMemoryFullException  {
365389    Request .Builder  uploadRequestBuilder  = new  Request .Builder ().url (photoUploadUrl );
366390    uploadRequestBuilder .header ("Authorization" , "Bearer "  + credential .getAccessToken ());
367391
@@ -372,7 +396,8 @@ private Response uploadChunk(DataChunk chunk, String photoUploadUrl, long totalF
372396
373397    // set chunk data headers, indicating size and chunk range 
374398    final  String  contentRange  =
375-         String .format ("bytes %d-%d/%d" , chunk .streamByteOffset (), chunk .finalByteOffset (), totalFileSize );
399+         String .format (
400+             "bytes %d-%d/%d" , chunk .streamByteOffset (), chunk .finalByteOffset (), totalFileSize );
376401    uploadRequestBuilder .header ("Content-Range" , contentRange );
377402    uploadRequestBuilder .header ("Content-Length" , String .format ("%d" , chunk .size ()));
378403
@@ -390,17 +415,23 @@ private Response uploadChunk(DataChunk chunk, String photoUploadUrl, long totalF
390415    }
391416    int  chunkCode  = chunkResponse .code ();
392417    if  (chunkCode  == 507  && chunkResponse .message ().contains ("Insufficient Storage" )) {
393-       throw  new  DestinationMemoryFullException ("Microsoft destination storage limit reached" ,
394-           new  IOException (String .format (
395-               "Got error HTTP status %d  with message: %s" , chunkCode , chunkResponse .message ())));
418+       throw  new  DestinationMemoryFullException (
419+           "Microsoft destination storage limit reached" ,
420+           new  IOException (
421+               String .format (
422+                   "Got error HTTP status %d  with message: %s" ,
423+                   chunkCode , chunkResponse .message ())));
396424    } else  if  (chunkCode  < 200  || chunkCode  > 299 ) {
397-       throw  new  IOException (String .format (
398-           "Got error HTTP status: %d message: \" %s\"  body: \" %s\" " , chunkCode , chunkResponse .message (),
399-           chunkResponse .body ().string ()));
425+       throw  new  IOException (
426+           String .format (
427+               "Got error HTTP status: %d message: \" %s\"  body: \" %s\" " ,
428+               chunkCode , chunkResponse .message (), chunkResponse .body ().string ()));
400429    } else  if  (chunkCode  == 200  || chunkCode  == 201  || chunkCode  == 202 ) {
401-       monitor .info (()
402-                        -> String .format ("Uploaded chunk range %d-%d (of total bytesize: %d) successfuly, HTTP status %d" ,
403-                            chunk .streamByteOffset (), chunk .finalByteOffset (), totalFileSize , chunkCode ));
430+       monitor .info (
431+           () ->
432+               String .format (
433+                   "Uploaded chunk range %d-%d (of total bytesize: %d) successfuly, HTTP status %d" ,
434+                   chunk .streamByteOffset (), chunk .finalByteOffset (), totalFileSize , chunkCode ));
404435    }
405436    return  chunkResponse ;
406437  }
0 commit comments