Skip to content

Commit

Permalink
feat(storage): add multi-bucket feature support (#5681)
Browse files Browse the repository at this point in the history
  • Loading branch information
NikaHsn authored Dec 9, 2024
1 parent 7bdb860 commit 5b7b82e
Show file tree
Hide file tree
Showing 53 changed files with 1,826 additions and 153 deletions.
34 changes: 30 additions & 4 deletions infra-gen2/backends/storage/main/amplify/backend.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,52 @@
import { defineBackend } from "@aws-amplify/backend";
import * as s3 from "aws-cdk-lib/aws-s3";
import { auth } from "./auth/resource";
import { storage } from "./storage/resource";
import { firstBucket, secondBucket } from "./storage/resource";

/**
* @see https://docs.amplify.aws/react/build-a-backend/ to add storage, functions, and more
*/
const backend = defineBackend({
auth,
storage,
firstBucket,
secondBucket,
});

// custom storage configurations
const s3Bucket = backend.storage.resources.bucket;
const s3Bucket = backend.firstBucket.resources.bucket;
const cfnBucket = s3Bucket.node.defaultChild as s3.CfnBucket;
const s3SecondaryBucket = backend.secondBucket.resources.bucket;
const cfnSecondaryBucket = s3SecondaryBucket.node.defaultChild as s3.CfnBucket;

cfnBucket.accelerateConfiguration = {
accelerationStatus: "Enabled",
};

cfnSecondaryBucket.accelerateConfiguration = {
accelerationStatus: "Enabled",
};

// required to add the metadata header, which amplify-backend does not support
backend.firstBucket.resources.cfnResources.cfnBucket.corsConfiguration = {
corsRules: [
{
allowedHeaders: ["*"],
allowedMethods: ["GET", "HEAD", "PUT", "POST", "DELETE"],
allowedOrigins: ["*"],
exposedHeaders: [
"x-amz-server-side-encryption",
"x-amz-request-id",
"x-amz-id-2",
"ETag",
"x-amz-meta-description",
],
maxAge: 3000,
},
],
};

// required to add the metadata header, which amplify-backend does not support
backend.storage.resources.cfnResources.cfnBucket.corsConfiguration = {
backend.secondBucket.resources.cfnResources.cfnBucket.corsConfiguration = {
corsRules: [
{
allowedHeaders: ["*"],
Expand Down
22 changes: 20 additions & 2 deletions infra-gen2/backends/storage/main/amplify/storage/resource.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
import { defineStorage } from "@aws-amplify/backend";

export const storage = defineStorage({
name: "Storage Integ Test main",
export const firstBucket = defineStorage({
name: "Storage Integ Test main bucket",
isDefault: true,
access: (allow) => ({
"public/*": [
allow.guest.to(["read", "write", "delete"]),
allow.authenticated.to(["read", "delete", "write"]),
],
"protected/{entity_id}/*": [
allow.authenticated.to(["read"]),
allow.entity("identity").to(["read", "write", "delete"]),
],
"private/{entity_id}/*": [
allow.entity("identity").to(["read", "write", "delete"]),
],
}),
});

export const secondBucket = defineStorage({
name: "Storage Integ Test secondary bucket",
access: (allow) => ({
"public/*": [
allow.guest.to(["read", "write", "delete"]),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import 'package:amplify_core/amplify_core.dart';

part 'bucket_outputs.g.dart';

/// {@template amplify_core.amplify_outputs.bucket_outputs}
/// The Amplify Gen 2 outputs for Buckets in the Storage category.
/// {@endtemplate}
@zAmplifyOutputsSerializable
class BucketOutputs
with AWSEquatable<BucketOutputs>, AWSSerializable, AWSDebuggable {
/// {@macro amplify_core.amplify_outputs.bucket_outputs}
const BucketOutputs({
required this.name,
required this.bucketName,
required this.awsRegion,
});

factory BucketOutputs.fromJson(Map<String, Object?> json) =>
_$BucketOutputsFromJson(json);

/// The user friendly name of the bucket
final String name;

/// The Amazon S3 bucket name.
final String bucketName;

/// The AWS region of Amazon S3 resources.
final String awsRegion;

@override
List<Object?> get props => [
name,
bucketName,
awsRegion,
];

@override
String get runtimeTypeName => 'BucketOutputs';

@override
Object? toJson() {
return _$BucketOutputsToJson(this);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import 'package:amplify_core/amplify_core.dart';
import 'package:amplify_core/src/config/amplify_outputs/storage/bucket_outputs.dart';

part 'storage_outputs.g.dart';

Expand All @@ -12,7 +13,11 @@ part 'storage_outputs.g.dart';
class StorageOutputs
with AWSEquatable<StorageOutputs>, AWSSerializable, AWSDebuggable {
/// {@macro amplify_core.amplify_outputs.storage_outputs}
const StorageOutputs({required this.awsRegion, required this.bucketName});
const StorageOutputs({
required this.awsRegion,
required this.bucketName,
this.buckets,
});

factory StorageOutputs.fromJson(Map<String, Object?> json) =>
_$StorageOutputsFromJson(json);
Expand All @@ -23,8 +28,11 @@ class StorageOutputs
/// The Amazon S3 bucket name.
final String bucketName;

/// The list of buckets if there are multiple buckets for the project
final List<BucketOutputs>? buckets;

@override
List<Object?> get props => [awsRegion, bucketName];
List<Object?> get props => [awsRegion, bucketName, buckets];

@override
String get runtimeTypeName => 'StorageOutputs';
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ part 'network_exception.dart';
part 'push/push_notification_exception.dart';
part 'storage/access_denied_exception.dart';
part 'storage/http_status_exception.dart';
part 'storage/invalid_storage_bucket_exception.dart';
part 'storage/local_file_not_found_exception.dart';
part 'storage/not_found_exception.dart';
part 'storage/operation_canceled_exception.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
part of '../amplify_exception.dart';

/// {@template amplify_core.storage.invalid_storage_bucket_exception}
/// Exception thrown when the [StorageBucket] is invalid.
/// {@endtemplate}
class InvalidStorageBucketException extends StorageException {
const InvalidStorageBucketException(
super.message, {
super.recoverySuggestion,
super.underlyingException,
});

@override
String get runtimeTypeName => 'InvalidStorageBucketException';
}
24 changes: 24 additions & 0 deletions packages/amplify_core/lib/src/types/storage/bucket_info.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:amplify_core/amplify_core.dart';

/// {@template amplify_core.storage.bucket_info}
/// Presents a storage bucket information.
/// {@endtemplate}
class BucketInfo
with AWSEquatable<BucketInfo>, AWSSerializable<Map<String, Object?>> {
/// {@macro amplify_core.storage.bucket_info}
const BucketInfo({required this.bucketName, required this.region});
final String bucketName;
final String region;

@override
List<Object?> get props => [
bucketName,
region,
];

@override
Map<String, Object?> toJson() => {
'bucketName': bucketName,
'region': region,
};
}
24 changes: 24 additions & 0 deletions packages/amplify_core/lib/src/types/storage/copy_buckets.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:amplify_core/amplify_core.dart';

/// Presents storage buckets for a copy operation.
class CopyBuckets with AWSSerializable<Map<String, Object?>> {
/// Creates a [CopyBuckets] with [source] and [destination] buckets.
const CopyBuckets({
required this.source,
required this.destination,
});

/// Creates a [CopyBuckets] with the same [bucket] for the [source] and [destination].
CopyBuckets.sameBucket(StorageBucket bucket)
: source = bucket,
destination = bucket;

final StorageBucket source;
final StorageBucket destination;

@override
Map<String, Object?> toJson() => {
'source': source.toJson(),
'destination': destination.toJson(),
};
}
13 changes: 10 additions & 3 deletions packages/amplify_core/lib/src/types/storage/copy_options.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import 'package:aws_common/aws_common.dart';
import 'package:amplify_core/amplify_core.dart';

/// {@template amplify_core.storage.copy_options}
/// Configurable options for `Amplify.Storage.copy`.
Expand All @@ -12,20 +12,27 @@ class StorageCopyOptions
AWSSerializable<Map<String, Object?>>,
AWSDebuggable {
/// {@macro amplify_core.storage.copy_options}
const StorageCopyOptions({this.pluginOptions});
const StorageCopyOptions({
this.pluginOptions,
this.buckets,
});

/// plugin specific options for `Amplify.Storage.copy`.
final StorageCopyPluginOptions? pluginOptions;

/// Optionally specify which buckets to target
final CopyBuckets? buckets;

@override
List<Object?> get props => [pluginOptions];
List<Object?> get props => [pluginOptions, buckets];

@override
String get runtimeTypeName => 'StorageCopyOptions';

@override
Map<String, Object?> toJson() => {
'pluginOptions': pluginOptions?.toJson(),
'buckets': buckets?.toJson(),
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import 'package:aws_common/aws_common.dart';
import 'package:amplify_core/amplify_core.dart';

/// {@template amplify_core.storage.download_data_options}
/// Configurable options for `Amplify.Storage.downloadData`.
Expand All @@ -14,20 +14,25 @@ class StorageDownloadDataOptions
/// {@macro amplify_core.storage.download_data_options}
const StorageDownloadDataOptions({
this.pluginOptions,
this.bucket,
});

/// {@macro amplify_core.storage.download_data_plugin_options}
final StorageDownloadDataPluginOptions? pluginOptions;

/// Optionally specify which bucket to target
final StorageBucket? bucket;

@override
List<Object?> get props => [pluginOptions];
List<Object?> get props => [pluginOptions, bucket];

@override
String get runtimeTypeName => 'StorageDownloadDataOptions';

@override
Map<String, Object?> toJson() => {
'pluginOptions': pluginOptions?.toJson(),
'bucket': bucket?.toJson(),
};
}

Expand Down
Loading

0 comments on commit 5b7b82e

Please sign in to comment.