package Ġorg . rap ture main . currency converter . service Ċ Ċ import Ġlombok . extern . slf 4 j . Slf 4 j Ċ import Ġorg . rap ture main . currency converter . exception . Currency Converter ApiException Ċ import Ġorg . rap ture main . currency converter . model . Currency Ċ import Ġorg . slf 4 j . LoggerFactory Ċ import Ġorg . springframework . beans . factory . annotation . Autowired Ċ import Ġorg . springframework . beans . factory . annotation . Value Ċ import Ġorg . springframework . http . HttpMethod Ċ import Ġorg . springframework . http . HttpStatus Ċ import Ġorg . springframework . stereotype . Component Ċ import Ġorg . springframework . web . client . Rest Template Ċ import Ġorg . springframework . web . client . exchange Ċ import Ġjava . math . BigDecimal Ċ import Ġjava . util . concurrent . TimeUnit Ċ Ċ @ Component Ċ @ Slf 4 j Ċ class ĠCurrency Converter ApiService Ċ @ Autowired Ġconstructor ( private Ġval Ġrest Template : ĠRest Template ) Ġ{ ĊĠĠĠ Ġprivate Ġval Ġlog Ġ= ĠLoggerFactory . getLogger ( this . java Class ) ĊĊĠĠĠ Ġprivate Ġval Ġexchange Rate Currency Encoding Template Ġ= Ġ"% s _% s " ĊĠĠĠ Ġprivate Ġval Ġget Exchange Rate Url Template Ġ= Ġ"% s / api / v 7 / convert ? q =% s & compact = ul tra & apiKey =% s " ĊĠĠĠ Ġprivate Ġval Ġget Available C urrencies Ġ= Ġ"% s / api / v 7 / currencies ? apiKey =% s " ĊĊĠĠĠ Ġ@ Value ("\ ${ currency Converter Api . url }") ĊĠĠĠ Ġprivate Ġlateinit Ġvar Ġapi Url : ĠString ĊĠĠĠ Ġ@ Value ("\ ${ currency Converter Api . apiKey }") ĊĠĠĠ Ġprivate Ġlateinit Ġvar ĠapiKey : ĠString ĊĠĠĠ Ġ@ Value ("\ ${ currency Converter Api . ttl In Minutes }") ĊĠĠĠ Ġprivate Ġlateinit Ġvar Ġcache TTL : ĠString ĊĊĠĠĠ Ġprivate Ġvar Ġavailable C urrencies : ĠSet < String >? Ġ= Ġnull ĊĠĠĠ Ġprivate Ġval Ġlock Ġ= ĠAny () ĊĊĠĠĠ Ġprivate Ġval Ġcache Ġ= Ġmutable Map Of < Pair < String , ĠString >, ĠPair < Long , ĠBigDecimal >>() ĊĊĠĠĠ Ġ@ Suppress (" UN CHECK ED _ CAST ") ĊĠĠĠ Ġfun Ġis Currency Available ( currency Code : ĠString ): ĠBoolean Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( available C urrencies Ġ== Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsynchronized ( lock ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( available C urrencies Ġ== Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġurl Ġ= ĠString . format ( get Available C urrencies , Ġapi Url , ĠapiKey ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġmap Ġ= Ġmake Request ( url ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġcurr Map Ġ= Ġmap [" results "] Ġas ĠMap < String , ĠAny > ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġavailable Ġ= Ġmutable ListOf < String >() ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcurr Map . entries . forEach Ġ{ Ġentry Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġavailable . add ( entry . key ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġavailable C urrencies Ġ= Ġavailable . toSet () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġreturn Ġavailable C urrencies !!. contains ( currency Code ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Throws ( Currency Converter ApiException :: class ) ĊĠĠĠ Ġfun Ġget Currency Exchange Rate ( currency From : ĠCurrency , Ġcurrency To : ĠCurrency ): ĠBigDecimal ? Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ(! is Currency Available ( currency From . currency Code ) Ġ|| Ġ! is Currency Available ( currency To . currency Code )) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġnull ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġval Ġcached Ġ= Ġget From Cache ( currency From , Ġcurrency To ) ĊĠĠĠĠĠĠĠ Ġif Ġ( cached Ġ!= Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġcached ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġval Ġcurrency Ġ= ĠString . format ( exchange Rate Currency Encoding Template , Ġcurrency From . currency Code , Ġcurrency To . currency Code ) ĊĠĠĠĠĠĠĠ Ġval Ġurl Ġ= ĠString . format ( get Exchange Rate Url Template , Ġapi Url , Ġcurrency , ĠapiKey ) ĊĠĠĠĠĠĠĠ Ġval Ġmap Ġ= Ġmake Request ( url ) ĊĊĠĠĠĠĠĠĠ Ġval Ġrate Ġ= Ġif Ġ( map [ currency ] Ġ== Ġnull ) Ġnull Ġelse ĠBigDecimal ( map [ currency ]. toString ()) ĊĊĠĠĠĠĠĠĠ Ġif Ġ( rate Ġ!= Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsave To Cache ( currency From , Ġcurrency To , Ġrate ) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġreturn Ġrate ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Throws ( Currency Converter ApiException :: class ) ĊĠĠĠ Ġprivate Ġfun Ġmake Request ( url : ĠString ): ĠMap < String , ĠAny > Ġ{ ĊĠĠĠĠĠĠĠ Ġlog . debug (" Sending ĠGET Ġrequest Ġto Ġ${ url . replace ( Regex ("[ & ? ] apiKey = .* $ "), Ġ"& apiKey =< API ĠKEY >") }") ĊĠĠĠĠĠĠĠ Ġval Ġresponse Ġ= Ġrest Template . exchange < Map < String , ĠAny >>( url , ĠHttpMethod . GET , Ġnull , ĠHashMap :: class ) ĊĠĠĠĠĠĠĠ Ġif Ġ( response . statusCode Ġ!= ĠHttpStatus . OK ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlog . error (" Curr Conv ĠAPI Ġfailed . ĠStatus Ġcode : Ġ[ {} ], ĠResponse : Ġ[ {} ]", Ġresponse . statusCode , Ġresponse . body ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthrow ĠCurrency Converter ApiException (" Returned Ġstatus Ġcode Ġis : Ġ${ response . statusCode }") ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġreturn Ġresponse . body !! ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġget From Cache ( currency From : ĠCurrency , Ġcurrency To : ĠCurrency ): ĠBigDecimal ? Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġcache Entry Ġ= Ġcache [ currency From . currency Code Ġto Ġcurrency To . currency Code ] Ġ?: Ġreturn Ġnull ĊĊĠĠĠĠĠĠĠ Ġif Ġ( System . currentTimeMillis () Ġ- Ġcache Entry . first Ġ> ĠTimeUnit . MIN UTES . to Millis ( cache TTL . to Long ())) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġnull ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġreturn Ġcache Entry . second ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġsave To Cache ( currency From : ĠCurrency , Ġcurrency To : ĠCurrency , Ġrate : ĠBigDecimal ) Ġ{ ĊĠĠĠĠĠĠĠ Ġcache [ currency From . currency Code Ġto Ġcurrency To . currency Code ] Ġ= ĠSystem . currentTimeMillis () Ġto Ġrate ĊĠĠĠ Ġ} Ċ }