Skip to content

Commit b161418

Browse files
committed
Add code snippet for s3: Minio bash and R aws.s3
1 parent 5f1e818 commit b161418

2 files changed

Lines changed: 165 additions & 2 deletions

File tree

web/src/core/usecases/s3ProfilesDetailsUiController/decoupledLogic/codeSnippets.ts

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
export 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+
146207
function 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+
343471
function 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+
491625
function 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+
495655
function trimCode(code: string): string {
496656
const lines = code.replace(/^\n+|\n+$/g, "").split("\n");
497657
const indents = lines

web/src/ui/shared/codex/s3ProfileDialog/S3ProfileDetails/S3ProfileDetails.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ function isTechnology(
649649
function getCodeTextEditorLanguage(technology: Technology): CodeTextEditorLanguage {
650650
switch (technology) {
651651
case "AWS CLI / shared profile":
652+
case "MinIO Client (bash)":
652653
return "shell";
653654
case "Python (boto3)":
654655
case "Python (s3fs)":
@@ -657,6 +658,7 @@ function getCodeTextEditorLanguage(technology: Technology): CodeTextEditorLangua
657658
return "python";
658659
case "DuckDB":
659660
return "SQL";
661+
case "R (aws.s3)":
660662
case "R (arrow)":
661663
case "R (paws)":
662664
return "R";
@@ -790,8 +792,9 @@ const useStyles = tss.withName({ S3ProfileDetails }).create(({ theme }) => ({
790792
flexShrink: 0
791793
},
792794
technologySelectControl: {
793-
flex: "0 1 220px",
794-
minWidth: 148,
795+
flex: "0 0 280px",
796+
minWidth: 280,
797+
maxWidth: "100%",
795798
"& .MuiInputBase-root": {
796799
color: theme.colors.useCases.typography.textPrimary
797800
}

0 commit comments

Comments
 (0)