/* Ċ Ġ* ĠCopyright Ġ 2 0 0 0 - 2 0 2 1 ĠJetBrains Ġs . r . o . Ċ Ġ* Ċ Ġ* ĠLicensed Ġunder Ġthe ĠApache ĠLicense , ĠVersion Ġ 2 . 0 Ġ( the Ġ" License "); Ċ Ġ* Ġyou Ġmay Ġnot Ġuse Ġthis Ġfile Ġexcept Ġin Ġcompliance Ġwith Ġthe ĠLicense . Ċ Ġ* ĠYou Ġmay Ġobtain Ġa Ġcopy Ġof Ġthe ĠLicense Ġat Ċ Ġ* Ċ Ġ* Ġhttp :// www . apache . org / licenses / LICENSE - 2 . 0 Ċ Ġ* Ċ Ġ* ĠUnless Ġrequired Ġby Ġapplicable Ġlaw Ġor Ġagreed Ġto Ġin Ġwriting , Ġsoftware Ċ Ġ* Ġdistributed Ġunder Ġthe ĠLicense Ġis Ġdistributed Ġon Ġan Ġ" AS ĠIS " ĠBASIS , Ċ Ġ* ĠWITHOUT ĠWARRANTIES ĠOR ĠCONDITIONS ĠOF ĠANY ĠKIND , Ġeither Ġexpress Ġor Ġimplied . Ċ Ġ* ĠSee Ġthe ĠLicense Ġfor Ġthe Ġspecific Ġlanguage Ġgoverning Ġpermissions Ġand Ċ Ġ* Ġlimitations Ġunder Ġthe ĠLicense . Ċ Ġ*/ Ċ Ċ package Ġjet brains . build Server . artifacts . google . publish Ċ Ċ import Ġcom . google . api . client . http .* Ċ import Ġcom . google . api . client . http . jav an et . Net Http Transport Ċ import Ġcom . google . api . client . util . Exponent ial Back Off Ċ import Ġcom . intellij . openapi . diagnostic . Logger Ċ import Ġjet brains . build Server . agent . Agent Running Build Ċ import Ġjet brains . build Server . agent . Artifact Publishing FailedException Ċ import Ġjet brains . build Server . artifacts . Artifact Data Instance Ċ import Ġjet brains . build Server . server Side . artifacts . google . Google Constants Ċ import Ġjet brains . build Server . server Side . artifacts . google . Google Signed Url Helper Ċ import Ġkotlinx . coroutines . experimental .* Ċ import Ġjava . io . File Ċ import Ġjava . io . IOException Ċ import Ġjava . util . concurrent . TimeUnit Ċ Ċ class ĠGoogle Signed Url File Uploader Ġ: ĠGoogle File Uploader Ġ{ ĊĊĠĠĠ Ġprivate Ġval Ġrequest Factory Ġ= ĠHTTP _ TRANSPORT . create Request Factory () ĊĊĠĠĠ Ġoverride Ġfun Ġpublish Files ( build : ĠAgent Running Build , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġpath Prefix : ĠString , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfiles To Publish : ĠMap < File , ĠString >) Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn @ run Blocking Ġfiles To Publish . map ({ Ġ( file , Ġpath ) Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpublish File Async ( build , Ġfile , Ġpath , Ġpath Prefix ) ĊĠĠĠĠĠĠĠ Ġ}). map Ġ{ Ġit . await () Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġpublish File Async ( build : ĠAgent Running Build , Ġfile : ĠFile , Ġpath : ĠString , Ġpath Prefix : ĠString ) Ġ= Ġasync ( Common Pool , ĠCor outine Start . DEFAULT ) Ġ{ ĊĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpublish File ( build , Ġfile , Ġpath , Ġpath Prefix ). await () ĊĠĠĠĠĠĠĠ Ġ} Ġcatch Ġ( e : ĠThrowable ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval ĠfilePath Ġ= ĠGoogle FileUtils . normalize Path ( path , Ġfile . name ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġmessage Ġ= Ġ" Failed Ġto Ġpublish Ġartifact Ġ$ filePath : Ġ${ e . message }" ĊĠĠĠĠĠĠĠĠĠĠĠ ĠLOG . info And Debug Details ( message , Ġe ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthrow ĠArtifact Publishing FailedException ( message , Ġfalse , Ġe ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ// ĠUpload Ġartifact Ġusing Ġres um able Ġmethod : ĊĠĠĠ Ġ// Ġhttps :// cloud . google . com / storage / docs / xml - api / res um able - upload ĊĠĠĠ Ġprivate Ġfun Ġpublish File ( build : ĠAgent Running Build , Ġfile : ĠFile , Ġpath : ĠString , Ġpath Prefix : ĠString ) Ġ= Ġasync ( Common Pool , ĠCor outine Start . DEFAULT ) Ġ{ ĊĠĠĠĠĠĠĠ Ġval ĠfilePath Ġ= ĠGoogle FileUtils . normalize Path ( path , Ġfile . name ) ĊĠĠĠĠĠĠĠ Ġval Ġblob Name Ġ= ĠGoogle FileUtils . normalize Path ( path Prefix , ĠfilePath ) ĊĠĠĠĠĠĠĠ Ġval ĠcontentType Ġ= ĠGoogle FileUtils . get ContentType ( file ) ĊĠĠĠĠĠĠĠ Ġval Ġsigned Url Ġ= Ġget Signed Url ( build , Ġblob Name , ĠcontentType ) ĊĠĠĠĠĠĠĠ Ġval Ġlocation Url Ġ= Ġget Location Url ( signed Url , ĠcontentType ) ĊĊĠĠĠĠĠĠĠ Ġvar Ġrange Ġ= Ġ 0 L ĊĠĠĠĠĠĠĠ Ġvar Ġresponse : ĠHttpResponse ĊĠĠĠĠĠĠĠ Ġval Ġback Off Ġ= ĠExp onent ial Back Off () ĊĠĠĠĠĠĠĠ Ġvar Ġback Off Interval : ĠLong ĊĠĠĠĠĠĠĠ Ġdo Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( range Ġ< Ġ 0 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġrange Ġ= ĠgetCurrent Range ( location Url , Ġfile ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġfile Length Ġ= Ġfile . length () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġcontent Ġ= ĠFile Range Content ( contentType , Ġfile ). apply Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġthis . range Ġ= Ġrange ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġput Request Ġ= Ġrequest Factory . build Put Request ( Generic Url ( location Url ), Ġcontent ). apply Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( range Ġ> Ġ 0 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġthis . headers . content Range Ġ= Ġ" bytes Ġ${ range Ġ+ Ġ 1 }-${ file Length Ġ- Ġ 1 }/ $ file Length " ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġresponse Ġ= Ġput Request . execute () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( response . is Success StatusCode ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġreturn @ async ĠArtifact Data Instance . create ( filePath , Ġfile Length ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġelse Ġif Ġ( Http Back Off Un successful Response Handler . Back Off Required . ON _ SERVER _ ERROR . isRequired ( response )) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġback Off Interval Ġ= Ġback Off . next Back Off Millis () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġerror Ġ= Ġget Http Error ( response ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( back Off Interval Ġ!= ĠExp onent ial Back Off . STOP ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġbuild . build Logger . message (" Failed Ġto Ġpublish Ġartifact Ġ$ filePath : Ġ$ error . ĠWill Ġretry Ġin Ġ${ back Off Interval Ġ/ Ġ 1 0 0 0 } Ġseconds .") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġdelay ( back Off Interval , ĠTimeUnit . MILLISECONDS ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġthrow ĠIOException ( error ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠLOG . debug (" Response Ġbody :\ n " Ġ+ Ġresponse . parse AsString ()) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġthrow ĠIOException (" Invalid Ġresponse Ġcode Ġ${ response . statusCode }. ") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrange Ġ= Ġ- 1 ĊĠĠĠĠĠĠĠ Ġ} Ġwhile Ġ( back Off Interval Ġ!= ĠExp onent ial Back Off . STOP ) ĊĊĠĠĠĠĠĠĠ Ġthrow ĠIOException (" Unable Ġto Ġcomplete Ġupload : Ġ${ get Http Error ( response )}, Ġbody : Ġ${ response . parse AsString () }") ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun ĠgetCurrent Range ( location Url : ĠString , Ġfile : ĠFile ): ĠLong Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġput Request Ġ= Ġrequest Factory . build Put Request ( Generic Url ( location Url ), ĠEmpty Content ()) ĊĠĠĠĠĠĠĠ Ġput Request . headers . content Range Ġ= Ġ" bytes Ġ*/ ${ file . length () }" ĊĊĠĠĠĠĠĠĠ Ġval Ġresponse Ġ= Ġput Request . execute () ĊĠĠĠĠĠĠĠ Ġif Ġ( response . statusCode Ġ!= Ġ 3 0 8 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthrow ĠIOException (" Can 't Ġget Ġcurrent Ġbytes Ġrange Ġfor Ġupload : Ġ${ get Http Error ( response )} ") ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġreturn Ġresponse . headers . range . substring (" bytes = 0 - ". length ). to Long () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġget Location Url ( signed Url : ĠString , ĠcontentType : ĠString ): ĠString Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġpost Request Ġ= Ġrequest Factory . build Post Request ( Generic Url ( signed Url ), ĠEmpty Content ()) ĊĠĠĠĠĠĠĠ Ġpost Request . un successful Response Handler Ġ= ĠHttp Back Off Un successful Response Handler ( Exponent ial Back Off ()) ĊĠĠĠĠĠĠĠ Ġpost Request . headers . contentType Ġ= ĠcontentType ĊĠĠĠĠĠĠĠ Ġpost Request . headers [" x - goog - res um able "] Ġ= Ġ" start " ĊĊĠĠĠĠĠĠĠ Ġval Ġresponse Ġ= Ġpost Request . execute () ĊĠĠĠĠĠĠĠ Ġif Ġ( response . statusCode Ġ!= Ġ 2 0 1 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthrow ĠIOException (" Can 't Ġget Ġupload Ġlocation ĠURL : Ġ${ get Http Error ( response )} ") ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġreturn Ġresponse . headers . location Ġ?: Ġthrow ĠIOException (" Can 't Ġget Ġupload Ġlocation ĠURL ") ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġget Signed Url ( build : ĠAgent Running Build , Ġblob Name : ĠString , ĠcontentType : ĠString ): ĠString Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġagent Configuration Ġ= Ġbuild . agent Configuration ĊĠĠĠĠĠĠĠ Ġval Ġtarget Url Ġ= Ġ"${ agent Configuration . server Url }/ http Auth / plugins /${ Google Constants . STORAGE _ TYPE }/${ Google Constants . SIGNED _ URL _ PATH }. html " ĊĠĠĠĠĠĠĠ Ġval Ġblob Paths Ġ= ĠGoogle Signed Url Helper . write Blob Paths ( map Of ( blob Name Ġto ĠcontentType )) ĊĊĠĠĠĠĠĠĠ Ġval Ġpost Request Ġ= Ġrequest Factory . build Post Request ( Generic Url ( target Url ), ĠByteArray Content ( APPLICATION _ XML , Ġblob Paths . toByteArray ())) ĊĠĠĠĠĠĠĠ Ġpost Request . un successful Response Handler Ġ= ĠHttp Back Off Un successful Response Handler ( Exponent ial Back Off ()) ĊĠĠĠĠĠĠĠ Ġpost Request . headers . set Basic Authentication ( build . access User , Ġbuild . access Code ) ĊĊĠĠĠĠĠĠĠ Ġval Ġresponse Ġ= Ġpost Request . execute () ĊĠĠĠĠĠĠĠ Ġif Ġ(! response . is Success StatusCode ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthrow ĠIOException (" Could Ġnot Ġget Ġsigned Ġupload ĠURL : Ġ${ get Http Error ( response )} ") ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġval Ġmapping Ġ= ĠGoogle Signed Url Helper . read Signed Url Mapping ( response . parse AsString ()) ĊĠĠĠĠĠĠĠ Ġreturn Ġmapping [ blob Name ] Ġ?: Ġthrow ĠIOException (" Could Ġnot Ġget Ġsigned Ġupload ĠURL : Ġno Ġinfo Ġfor Ġblob Ġ$ blob Name ") ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġget Http Error ( response : ĠHttpResponse ) Ġ= Ġ"${ response . status Message } Ġ( HTTP Ġ${ response . statusCode })" ĊĊĠĠĠ Ġcompanion Ġobject Ġ{ ĊĠĠĠĠĠĠĠ Ġprivate Ġval ĠLOG Ġ= ĠLogger . getInstance ( Google Signed Url File Uploader :: class . java . name ) ĊĠĠĠĠĠĠĠ Ġprivate Ġconst Ġval ĠAP PLICATION _ XML Ġ= Ġ" application / xml " ĊĠĠĠĠĠĠĠ Ġval ĠHTTP _ TRANSPORT : ĠHttp Transport Ġ= ĠNet Http Transport () ĊĠĠĠ Ġ} Ċ }