V0.6.2 #14
+100
@@ -1,6 +1,7 @@
|
||||
module garageS3
|
||||
|
||||
export
|
||||
LifecycleExpiration, build_lifecycle_xml,
|
||||
GarageStorage, put_file, get_file, list_files, delete_file,
|
||||
set_lifecycle_expiration, get_lifecycle, delete_lifecycle
|
||||
|
||||
@@ -266,11 +267,110 @@ function delete_file(storage::GarageStorage, key::String)
|
||||
end
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# 3. Bucket Lifecycle Configuration
|
||||
# ===================================================================
|
||||
|
||||
""" Lifecycle expiration configuration for a bucket.
|
||||
|
||||
# Fields
|
||||
- `prefix::String`: Object key prefix this rule applies to.
|
||||
- `days::Int`: Number of days after object creation to expire/delete.
|
||||
- `enabled::Bool`: Whether the rule is active (default: `true`).
|
||||
- `id::String`: Unique identifier for the rule (default: auto-generated).
|
||||
"""
|
||||
struct LifecycleExpiration
|
||||
prefix::String
|
||||
days::Int
|
||||
enabled::Bool
|
||||
id::String
|
||||
end
|
||||
function LifecycleExpiration(prefix::String, days::Int; enabled::Bool=true, id::String="")
|
||||
rule_id = isempty(id) ? "expire-" * replace(prefix, "/" => "_") * "-" * string(days) * "d" : id
|
||||
return LifecycleExpiration(prefix, days, enabled, rule_id)
|
||||
end
|
||||
|
||||
""" Build the XML body for a PutBucketLifecycleConfiguration request.
|
||||
|
||||
Supports a single rule with expiration by prefix.
|
||||
"""
|
||||
function build_lifecycle_xml(rule::LifecycleExpiration)::String
|
||||
status = rule.enabled ? "Enabled" : "Disabled"
|
||||
return """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LifecycleConfiguration>
|
||||
<Rule>
|
||||
<ID>$(rule.id)</ID>
|
||||
<Filter>
|
||||
<Prefix>$(rule.prefix)</Prefix>
|
||||
</Filter>
|
||||
<Status>$status</Status>
|
||||
<Expiration>
|
||||
<Days>$(rule.days)</Days>
|
||||
</Expiration>
|
||||
</Rule>
|
||||
</LifecycleConfiguration>"""
|
||||
end
|
||||
|
||||
""" Set bucket lifecycle expiration rule.
|
||||
|
||||
# Arguments
|
||||
- `storage::GarageStorage`: The storage client instance.
|
||||
- `rule::LifecycleExpiration`: The lifecycle expiration rule.
|
||||
|
||||
# Returns
|
||||
- `Dict{String, Any}`: Parsed response from the server.
|
||||
|
||||
# Example
|
||||
```julia
|
||||
rule = LifecycleExpiration("uploads/", 30)
|
||||
set_lifecycle_expiration(storage, rule)
|
||||
```
|
||||
"""
|
||||
function set_lifecycle_expiration(storage::GarageStorage, rule::LifecycleExpiration)
|
||||
xml_body = build_lifecycle_xml(rule)
|
||||
args = Dict{String,Any}("body" => xml_body)
|
||||
return S3.put_bucket_lifecycle_configuration(storage.bucket, args; aws_config=storage.config)
|
||||
end
|
||||
|
||||
""" Get the bucket lifecycle configuration.
|
||||
|
||||
# Arguments
|
||||
- `storage::GarageStorage`: The storage client instance.
|
||||
|
||||
# Returns
|
||||
- `Dict{String, Any}`: Parsed lifecycle configuration, or `nothing` if no lifecycle is configured.
|
||||
|
||||
# Example
|
||||
```julia
|
||||
config = get_lifecycle(storage)
|
||||
```
|
||||
"""
|
||||
function get_lifecycle(storage::GarageStorage)::Union{Dict{String,Any},Nothing}
|
||||
try
|
||||
return S3.get_bucket_lifecycle_configuration(storage.bucket; aws_config=storage.config)
|
||||
catch e
|
||||
if isa(e, AWS.AWSException) && e.code == "NoSuchLifecycleConfiguration"
|
||||
return nothing
|
||||
end
|
||||
rethrow(e)
|
||||
end
|
||||
end
|
||||
|
||||
""" Delete the bucket lifecycle configuration.
|
||||
|
||||
# Arguments
|
||||
- `storage::GarageStorage`: The storage client instance.
|
||||
|
||||
# Returns
|
||||
- `Dict{String, Any}`: Parsed response from the server.
|
||||
|
||||
# Example
|
||||
```julia
|
||||
delete_lifecycle(storage)
|
||||
```
|
||||
"""
|
||||
function delete_lifecycle(storage::GarageStorage)
|
||||
return S3.delete_bucket_lifecycle(storage.bucket; aws_config=storage.config)
|
||||
end
|
||||
|
||||
end # module GarageS3
|
||||
|
||||
@@ -343,4 +343,121 @@ using UUIDs: uuid4
|
||||
delete_file(storage, test_key)
|
||||
end
|
||||
|
||||
# --- Lifecycle configuration tests ---
|
||||
|
||||
@testset "LifecycleExpiration construction with default ID" begin
|
||||
rule = LifecycleExpiration("uploads/", 30)
|
||||
@test rule.prefix == "uploads/"
|
||||
@test rule.days == 30
|
||||
@test rule.enabled == true
|
||||
@test rule.id == "expire-uploads_-30d"
|
||||
end
|
||||
|
||||
@testset "LifecycleExpiration construction with custom ID" begin
|
||||
rule = LifecycleExpiration("data/", 7; id="my-rule")
|
||||
@test rule.prefix == "data/"
|
||||
@test rule.days == 7
|
||||
@test rule.enabled == true
|
||||
@test rule.id == "my-rule"
|
||||
end
|
||||
|
||||
@testset "LifecycleExpiration with disabled flag" begin
|
||||
rule = LifecycleExpiration("temp/", 1; enabled=false)
|
||||
@test rule.enabled == false
|
||||
end
|
||||
|
||||
@testset "LifecycleExpiration with nested prefix" begin
|
||||
rule = LifecycleExpiration("deep/nested/", 90)
|
||||
@test rule.prefix == "deep/nested/"
|
||||
end
|
||||
|
||||
@testset "build_lifecycle_xml generates valid XML" begin
|
||||
rule = LifecycleExpiration("test/", 14; id="test-rule")
|
||||
xml = build_lifecycle_xml(rule)
|
||||
@test occursin("<?xml version=\"1.0\"", xml)
|
||||
@test occursin("<LifecycleConfiguration>", xml)
|
||||
@test occursin("<ID>test-rule</ID>", xml)
|
||||
@test occursin("<Prefix>test/</Prefix>", xml)
|
||||
@test occursin("<Status>Enabled</Status>", xml)
|
||||
@test occursin("<Days>14</Days>", xml)
|
||||
@test occursin("</LifecycleConfiguration>", xml)
|
||||
end
|
||||
|
||||
@testset "build_lifecycle_xml generates disabled rule XML" begin
|
||||
rule = LifecycleExpiration("temp/", 1; enabled=false)
|
||||
xml = build_lifecycle_xml(rule)
|
||||
@test occursin("<Status>Disabled</Status>", xml)
|
||||
end
|
||||
|
||||
@testset "integration: lifecycle CRUD - set and get" begin
|
||||
storage = GarageStorage(
|
||||
"https://s3-api.yiem.cc",
|
||||
"GKb080154a2e5b19100b1b2c6e",
|
||||
"a2c6b1379c2f731d3e6e5a408dd4d6cffca7511717675f55114ff94828febca1",
|
||||
"testbucket"
|
||||
)
|
||||
|
||||
# Should return nothing when no lifecycle is set
|
||||
lc = get_lifecycle(storage)
|
||||
@test lc === nothing
|
||||
|
||||
# Set lifecycle
|
||||
rule = LifecycleExpiration("lifecycle-test/", 45)
|
||||
set_lifecycle_expiration(storage, rule)
|
||||
|
||||
# Should return configuration after setting
|
||||
lc = get_lifecycle(storage)
|
||||
@test lc !== nothing
|
||||
@test haskey(lc, "Rule")
|
||||
|
||||
# Delete lifecycle
|
||||
delete_lifecycle(storage)
|
||||
|
||||
# Should return nothing after deletion
|
||||
lc = get_lifecycle(storage)
|
||||
@test lc === nothing
|
||||
end
|
||||
|
||||
@testset "integration: lifecycle with nested prefix" begin
|
||||
storage = GarageStorage(
|
||||
"https://s3-api.yiem.cc",
|
||||
"GKb080154a2e5b19100b1b2c6e",
|
||||
"a2c6b1379c2f731d3e6e5a408dd4d6cffca7511717675f55114ff94828febca1",
|
||||
"testbucket"
|
||||
)
|
||||
|
||||
rule = LifecycleExpiration("a/b/c/", 20; id="nested-rule")
|
||||
set_lifecycle_expiration(storage, rule)
|
||||
|
||||
lc = get_lifecycle(storage)
|
||||
@test lc !== nothing
|
||||
@test haskey(lc, "Rule")
|
||||
|
||||
delete_lifecycle(storage)
|
||||
@test get_lifecycle(storage) === nothing
|
||||
end
|
||||
|
||||
@testset "integration: lifecycle enabled/disabled toggle" begin
|
||||
storage = GarageStorage(
|
||||
"https://s3-api.yiem.cc",
|
||||
"GKb080154a2e5b19100b1b2c6e",
|
||||
"a2c6b1379c2f731d3e6e5a408dd4d6cffca7511717675f55114ff94828febca1",
|
||||
"testbucket"
|
||||
)
|
||||
|
||||
# Set enabled rule
|
||||
rule1 = LifecycleExpiration("toggle/", 10; enabled=true)
|
||||
set_lifecycle_expiration(storage, rule1)
|
||||
lc = get_lifecycle(storage)
|
||||
@test lc !== nothing
|
||||
|
||||
# Override with disabled rule
|
||||
rule2 = LifecycleExpiration("toggle/", 10; enabled=false)
|
||||
set_lifecycle_expiration(storage, rule2)
|
||||
lc = get_lifecycle(storage)
|
||||
@test lc !== nothing
|
||||
|
||||
delete_lifecycle(storage)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user