Skip to content

Commit

Permalink
Add forcePathStyle option (#476)
Browse files Browse the repository at this point in the history
* Add forcePathStyle option
  • Loading branch information
andrzejressel committed Apr 26, 2024
1 parent 9ae0308 commit 8a217c8
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 18 deletions.
13 changes: 7 additions & 6 deletions README.md
Expand Up @@ -40,15 +40,15 @@ All available s3 combinators and operations are available in the package object
In order to use this library, we need to add the following line in our `build.sbt` file:

```scala
libraryDependencies += "dev.zio" %% "zio-s3" % "0.4.2.4"
libraryDependencies += "dev.zio" %% "zio-s3" % "0.4.3"
```

## Example 1

Let's try an example of creating a bucket and adding an object into it. To run this example, we need to run an instance of _Minio_ which is object storage compatible with S3:

```bash
docker run -p 9000:9000 -e MINIO_ACCESS_KEY=MyKey -e MINIO_SECRET_KEY=MySecret minio/minio server --compat /data
docker run -p 9000:9000 -e MINIO_ROOT_USER=MyKey -e MINIO_ROOT_PASSWORD=MySecret minio/minio server --compat /data
```

In this example we create a bucket and then add a JSON object to it and then retrieve that:
Expand All @@ -59,7 +59,7 @@ import software.amazon.awssdk.regions.Region
import zio._
import zio.s3._
import zio.stream.{ZStream, ZPipeline}
import zio.{Chunk, ExitCode, URIO}
import zio.Chunk

import java.net.URI

Expand All @@ -71,7 +71,7 @@ object ZIOS3Example extends ZIOAppDefault {
_ <- putObject(
bucketName = "docs",
key = "doc1",
contentLength = json.length,
contentLength = json.length.toLong,
content = ZStream.fromChunk(json),
options = UploadOptions.fromContentType("application/json")
)
Expand All @@ -86,7 +86,8 @@ object ZIOS3Example extends ZIOAppDefault {
live(
Region.CA_CENTRAL_1,
AwsBasicCredentials.create("MyKey", "MySecret"),
Some(URI.create("http://localhost:9000"))
Some(URI.create("http://localhost:9000")),
forcePathStyle = Some(true) // Required for path-style S3 requests (MinIO by default uses them)
)
)
}
Expand All @@ -105,7 +106,7 @@ val json: Chunk[Byte] = Chunk.fromArray("""{ "id" : 1 , "name" : "A1" }""".getB
val up: ZIO[S3, S3Exception, Unit] = putObject(
"bucket-1",
"user.json",
json.length,
json.length.toLong,
ZStream.fromChunk(json),
UploadOptions.fromContentType("application/json")
)
Expand Down
13 changes: 7 additions & 6 deletions docs/index.md
Expand Up @@ -40,15 +40,15 @@ All available s3 combinators and operations are available in the package object
In order to use this library, we need to add the following line in our `build.sbt` file:

```scala
libraryDependencies += "dev.zio" %% "zio-s3" % "@VERSION@"
libraryDependencies += "dev.zio" %% "zio-s3" % "@VERSION@"
```

## Example 1

Let's try an example of creating a bucket and adding an object into it. To run this example, we need to run an instance of _Minio_ which is object storage compatible with S3:

```bash
docker run -p 9000:9000 -e MINIO_ACCESS_KEY=MyKey -e MINIO_SECRET_KEY=MySecret minio/minio server --compat /data
docker run -p 9000:9000 -e MINIO_ROOT_USER=MyKey -e MINIO_ROOT_PASSWORD=MySecret minio/minio server --compat /data
```

In this example we create a bucket and then add a JSON object to it and then retrieve that:
Expand All @@ -59,7 +59,7 @@ import software.amazon.awssdk.regions.Region
import zio._
import zio.s3._
import zio.stream.{ZStream, ZPipeline}
import zio.{Chunk, ExitCode, URIO}
import zio.Chunk

import java.net.URI

Expand All @@ -71,7 +71,7 @@ object ZIOS3Example extends ZIOAppDefault {
_ <- putObject(
bucketName = "docs",
key = "doc1",
contentLength = json.length,
contentLength = json.length.toLong,
content = ZStream.fromChunk(json),
options = UploadOptions.fromContentType("application/json")
)
Expand All @@ -86,7 +86,8 @@ object ZIOS3Example extends ZIOAppDefault {
live(
Region.CA_CENTRAL_1,
AwsBasicCredentials.create("MyKey", "MySecret"),
Some(URI.create("http://localhost:9000"))
Some(URI.create("http://localhost:9000")),
forcePathStyle = Some(true) // Required for path-style S3 requests (MinIO by default uses them)
)
)
}
Expand All @@ -105,7 +106,7 @@ val json: Chunk[Byte] = Chunk.fromArray("""{ "id" : 1 , "name" : "A1" }""".getB
val up: ZIO[S3, S3Exception, Unit] = putObject(
"bucket-1",
"user.json",
json.length,
json.length.toLong,
ZStream.fromChunk(json),
UploadOptions.fromContentType("application/json")
)
Expand Down
4 changes: 3 additions & 1 deletion zio-s3/src/main/scala/zio/s3/Live.scala
Expand Up @@ -234,7 +234,8 @@ object Live {
def connect[R](
region: S3Region,
provider: RIO[R with Scope, AwsCredentialsProvider],
uriEndpoint: Option[URI]
uriEndpoint: Option[URI],
forcePathStyle: Option[Boolean] = None
): ZIO[R with Scope, ConnectionError, S3] =
for {
credentials <- provider.mapError(e => ConnectionError(e.getMessage, e.getCause))
Expand All @@ -244,6 +245,7 @@ object Live {
.credentialsProvider(credentials)
.region(region.region)
uriEndpoint.foreach(builder.endpointOverride)
forcePathStyle.foreach(builder.forcePathStyle(_))
builder
}
service <- connect(builder)
Expand Down
14 changes: 10 additions & 4 deletions zio-s3/src/main/scala/zio/s3/package.scala
Expand Up @@ -35,18 +35,24 @@ package object s3 {
def settings[R](region: Region, cred: ZIO[R, S3Exception, AwsCredentials]): ZLayer[R, S3Exception, S3Settings] =
ZLayer(cred.flatMap(S3Settings.from(region, _)))

def live(region: Region, credentials: AwsCredentials, uriEndpoint: Option[URI] = None): Layer[S3Exception, S3] =
liveZIO(region, const(credentials), uriEndpoint)
def live(
region: Region,
credentials: AwsCredentials,
uriEndpoint: Option[URI] = None,
forcePathStyle: Option[Boolean] = None
): Layer[S3Exception, S3] =
liveZIO(region, const(credentials), uriEndpoint, forcePathStyle)

def liveZIO[R](
region: Region,
provider: RIO[R, AwsCredentialsProvider],
uriEndpoint: Option[URI] = None
uriEndpoint: Option[URI] = None,
forcePathStyle: Option[Boolean] = None
): ZLayer[R, S3Exception, S3] =
ZLayer.scoped[R](
ZIO
.fromEither(S3Region.from(region))
.flatMap(Live.connect[R](_, provider, uriEndpoint))
.flatMap(Live.connect[R](_, provider, uriEndpoint, forcePathStyle))
)

val live: ZLayer[S3Settings, ConnectionError, S3] = ZLayer.scoped(
Expand Down
18 changes: 17 additions & 1 deletion zio-s3/src/test/scala/zio/s3/S3Test.scala
Expand Up @@ -30,6 +30,22 @@ object S3LiveSpec extends ZIOSpecDefault {
S3Suite.spec("S3LiveSpec").provideLayerShared(s3)
}

object S3LiveHostnameSpec extends ZIOSpecDefault {

private val s3 =
zio.s3
.live(
Region.CA_CENTRAL_1,
AwsBasicCredentials.create("TESTKEY", "TESTSECRET"),
Some(URI.create("http://localhost:9000")),
forcePathStyle = Some(true)
)
.mapError(TestFailure.die)

override def spec: Spec[TestEnvironment with Scope, Any] =
S3Suite.spec("S3LiveHostnameSpec").provideLayerShared(s3)
}

object S3TestSpec extends ZIOSpecDefault {
private val root = ZPath("../test-data")

Expand All @@ -44,7 +60,7 @@ object InvalidS3LayerTestSpec extends ZIOSpecDefault {
private val s3: ZLayer[Scope, S3Exception, S3] =
zio.s3.liveZIO(Region.EU_CENTRAL_1, providers.default)

override def spec =
override def spec: Spec[TestEnvironment with Scope, Any] =
suite("InvalidS3LayerTest") {
test("listBuckets") {
listBuckets.provideLayer(s3).either.map(assert(_)(isLeft(isSubtype[S3Exception](anything))))
Expand Down

0 comments on commit 8a217c8

Please sign in to comment.