11export const technologies = [
22 "AWS CLI / shared profile" ,
3+ "MinIO Client (bash)" ,
34 "Python (boto3)" ,
45 "Python (s3fs)" ,
56 "Python (polars)" ,
67 "Python (pyarrow)" ,
78 "DuckDB" ,
9+ "R (aws.s3)" ,
810 "R (arrow)" ,
911 "R (paws)" ,
1012 "rclone"
@@ -34,6 +36,8 @@ type SnippetContext = {
3436 endpointScheme : "http" | "https" ;
3537 awsConfigSectionName : string ;
3638 awsServiceSectionName : string ;
39+ minioClientAlias : string ;
40+ minioClientEnvVarName : string ;
3741 rcloneRemoteName : string ;
3842 accessCredentials : AccessCredentials ;
3943} ;
@@ -53,11 +57,13 @@ export function getCodeSnippet(params: {
5357
5458 const snippetFactories = {
5559 "AWS CLI / shared profile" : getAwsCliSnippet ,
60+ "MinIO Client (bash)" : getMinioClientSnippet ,
5661 "Python (boto3)" : getBoto3Snippet ,
5762 "Python (s3fs)" : getS3fsSnippet ,
5863 "Python (polars)" : getPolarsSnippet ,
5964 "Python (pyarrow)" : getPyArrowSnippet ,
6065 DuckDB : getDuckDbSnippet ,
66+ "R (aws.s3)" : getRAwsS3Snippet ,
6167 "R (arrow)" : getRArrowSnippet ,
6268 "R (paws)" : getRPawsSnippet ,
6369 rclone : getRcloneSnippet
@@ -76,6 +82,7 @@ function getSnippetContext(params: {
7682
7783 const normalizedEndpointUrl = normalizeEndpointUrl ( endpointUrl ) ;
7884 const parsedEndpointUrl = new URL ( normalizedEndpointUrl ) ;
85+ const minioClientAlias = toMinioClientAlias ( profileName ) ;
7986
8087 return {
8188 profileName,
@@ -87,6 +94,8 @@ function getSnippetContext(params: {
8794 awsConfigSectionName :
8895 profileName === "default" ? "default" : `profile ${ profileName } ` ,
8996 awsServiceSectionName : `${ toSafeIdentifier ( profileName , "-" ) } -s3` ,
97+ minioClientAlias,
98+ minioClientEnvVarName : `MC_HOST_${ minioClientAlias } ` ,
9099 rcloneRemoteName : toSafeIdentifier ( profileName , "-" ) ,
91100 accessCredentials
92101 } ;
@@ -143,6 +152,58 @@ function getAwsCliSnippet(context: SnippetContext): CodeSnippet {
143152 } ;
144153}
145154
155+ function getMinioClientSnippet ( context : SnippetContext ) : CodeSnippet {
156+ const { accessCredentials } = context ;
157+
158+ if ( accessCredentials === undefined ) {
159+ return {
160+ fileBasename : "public-bucket-mc.sh" ,
161+ codeSrc : trimCode ( `
162+ # MinIO Client aliases require access and secret keys.
163+ # For anonymous public buckets, use the AWS CLI / shared profile snippet
164+ # or select a credentialed profile and regenerate this snippet.
165+ ` )
166+ } ;
167+ }
168+
169+ const { sessionToken } = accessCredentials ;
170+
171+ if ( sessionToken !== undefined ) {
172+ return {
173+ fileBasename : "setup-mc-alias.sh" ,
174+ codeSrc : trimCode ( `
175+ # Re-run this export when the session token is renewed.
176+ export ${ context . minioClientEnvVarName } =${ shellQuote (
177+ getMinioClientHostUrl ( {
178+ context,
179+ accessCredentials : {
180+ ...accessCredentials ,
181+ sessionToken
182+ }
183+ } )
184+ ) }
185+
186+ mc ls ${ shellQuote ( `${ context . minioClientAlias } /your-bucket` ) }
187+ ` )
188+ } ;
189+ }
190+
191+ return {
192+ fileBasename : "setup-mc-alias.sh" ,
193+ codeSrc : trimCode ( `
194+ mc alias set \\
195+ ${ shellQuote ( context . minioClientAlias ) } \\
196+ ${ shellQuote ( context . endpointOrigin ) } \\
197+ ${ shellQuote ( accessCredentials . accessKeyId ) } \\
198+ ${ shellQuote ( accessCredentials . secretAccessKey ) } \\
199+ --api S3v4
200+
201+ mc ls ${ shellQuote ( context . minioClientAlias ) }
202+ mc ls ${ shellQuote ( `${ context . minioClientAlias } /your-bucket` ) }
203+ ` )
204+ } ;
205+ }
206+
146207function getBoto3Snippet ( context : SnippetContext ) : CodeSnippet {
147208 return {
148209 fileBasename : "example.py" ,
@@ -340,6 +401,73 @@ function getDuckDbSnippet(context: SnippetContext): CodeSnippet {
340401 } ;
341402}
342403
404+ function getRAwsS3Snippet ( context : SnippetContext ) : CodeSnippet {
405+ const awsS3PackageRegion = getAwsS3PackageRegion ( context ) ;
406+ const useHttps = toRBoolean ( context . endpointScheme === "https" ) ;
407+
408+ return {
409+ fileBasename : "example.R" ,
410+ codeSrc :
411+ context . accessCredentials === undefined
412+ ? trimCode ( `
413+ install.packages("aws.s3", repos = "https://cloud.r-project.org")
414+
415+ Sys.setenv(
416+ AWS_S3_ENDPOINT = ${ JSON . stringify ( context . endpointAuthority ) }
417+ )
418+
419+ library(aws.s3)
420+
421+ bucket <- "your-bucket"
422+ objects <- get_bucket(
423+ bucket = bucket,
424+ max = 10,
425+ region = ${ JSON . stringify ( awsS3PackageRegion ) } ,
426+ use_https = ${ useHttps } ,
427+ key = "",
428+ secret = "",
429+ session_token = ""
430+ )
431+
432+ keys <- vapply(objects, function(item) item$Key, character(1))
433+ print(keys)
434+ ` )
435+ : trimCode ( `
436+ install.packages("aws.s3", repos = "https://cloud.r-project.org")
437+
438+ Sys.setenv(
439+ AWS_S3_ENDPOINT = ${ JSON . stringify ( context . endpointAuthority ) } ,
440+ AWS_ACCESS_KEY_ID = ${ JSON . stringify (
441+ context . accessCredentials . accessKeyId
442+ ) } ,
443+ AWS_SECRET_ACCESS_KEY = ${ JSON . stringify (
444+ context . accessCredentials . secretAccessKey
445+ ) } ${
446+ context . accessCredentials . sessionToken === undefined
447+ ? ""
448+ : `,
449+ AWS_SESSION_TOKEN = ${ JSON . stringify (
450+ context . accessCredentials . sessionToken
451+ ) } `
452+ }
453+ )
454+
455+ library(aws.s3)
456+
457+ bucket <- "your-bucket"
458+ objects <- get_bucket(
459+ bucket = bucket,
460+ max = 10,
461+ region = ${ JSON . stringify ( awsS3PackageRegion ) } ,
462+ use_https = ${ useHttps }
463+ )
464+
465+ keys <- vapply(objects, function(item) item$Key, character(1))
466+ print(keys)
467+ ` )
468+ } ;
469+ }
470+
343471function getRArrowSnippet ( context : SnippetContext ) : CodeSnippet {
344472 return {
345473 fileBasename : "example.R" ,
@@ -488,10 +616,42 @@ function toSafeIdentifier(value: string, separator: "-" | "_"): string {
488616 return normalized === "" ? "onyxia" : normalized ;
489617}
490618
619+ function toMinioClientAlias ( value : string ) : string {
620+ const safeIdentifier = toSafeIdentifier ( value , "_" ) ;
621+
622+ return / ^ [ a - z ] / . test ( safeIdentifier ) ? safeIdentifier : `onyxia_${ safeIdentifier } ` ;
623+ }
624+
491625function toSqlString ( value : string ) : string {
492626 return `'${ value . replace ( / ' / g, "''" ) } '` ;
493627}
494628
629+ function toRBoolean ( value : boolean ) : "TRUE" | "FALSE" {
630+ return value ? "TRUE" : "FALSE" ;
631+ }
632+
633+ function getAwsS3PackageRegion ( context : SnippetContext ) : string {
634+ return context . endpointAuthority === "s3.amazonaws.com" ? context . region : "" ;
635+ }
636+
637+ function getMinioClientHostUrl ( params : {
638+ context : SnippetContext ;
639+ accessCredentials : {
640+ accessKeyId : string ;
641+ secretAccessKey : string ;
642+ sessionToken : string ;
643+ } ;
644+ } ) : string {
645+ const { context, accessCredentials } = params ;
646+ const userInfo = [
647+ accessCredentials . accessKeyId ,
648+ accessCredentials . secretAccessKey ,
649+ accessCredentials . sessionToken
650+ ] . join ( ":" ) ;
651+
652+ return `${ context . endpointScheme } ://${ userInfo } @${ context . endpointAuthority } ` ;
653+ }
654+
495655function trimCode ( code : string ) : string {
496656 const lines = code . replace ( / ^ \n + | \n + $ / g, "" ) . split ( "\n" ) ;
497657 const indents = lines
0 commit comments