azure-blob 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +17 -6
- data/lib/azure_blob/blob.rb +2 -2
- data/lib/azure_blob/client.rb +67 -11
- data/lib/azure_blob/const.rb +8 -2
- data/lib/azure_blob/container.rb +33 -0
- data/lib/azure_blob/entra_id_signer.rb +3 -1
- data/lib/azure_blob/http.rb +16 -7
- data/lib/azure_blob/tags.rb +35 -0
- data/lib/azure_blob/user_delegation_key.rb +1 -1
- data/lib/azure_blob/version.rb +1 -1
- metadata +4 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: c6c4e076840dd6671c28c99b5fca03057782e81686c0c06890b42cedd3b83788
         | 
| 4 | 
            +
              data.tar.gz: '00668582cecce526ad816a5c537469af7e646d6f64d7392655d1c7e60396393c'
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: fdd32b7f8547ac428bf0eb19d884860e94d1ab9471df0022356119c5ec28aca8589d1b1925d0085b2361c4c16d5898123a1fc18f57b5b2cb9ae7e53dbc4b4379
         | 
| 7 | 
            +
              data.tar.gz: 44a4592046d5d33432762cfd4d3636500f9c13db00cf5c705fbc2f472b7c322bd37bc05d9765e03079e0966dcb44d0e5cc844db303906aaa18435c8db7ee422f
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,18 @@ | |
| 1 1 | 
             
            ## [Unreleased]
         | 
| 2 2 |  | 
| 3 | 
            +
             | 
| 4 | 
            +
            ## [0.5.3] 2024-10-31
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            - Add support for setting tags when uploading a blob
         | 
| 7 | 
            +
            - Add get_blob_tags
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ## [0.5.2] 2024-09-12
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            - Add get_container_properties
         | 
| 12 | 
            +
            - Add create_container
         | 
| 13 | 
            +
            - Add delete_container
         | 
| 14 | 
            +
            - Support for Azure China, US Gov and Germany
         | 
| 15 | 
            +
             | 
| 3 16 | 
             
            ## [0.5.1] 2024-09-09
         | 
| 4 17 |  | 
| 5 18 | 
             
            - Remove dev files from the release
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,6 +1,8 @@ | |
| 1 1 | 
             
            # AzureBlob
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 3 | 
            +
            Azure Blob client and Active Storage adapter to replace the now abandoned azure-storage-blob
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            An Active Storage is supplied, but the gem is Rails agnostic and can be used in any Ruby project.
         | 
| 4 6 |  | 
| 5 7 | 
             
            ## Active Storage
         | 
| 6 8 |  | 
| @@ -12,6 +14,16 @@ To migrate from azure-storage-blob to azure-blob: | |
| 12 14 | 
             
            3. Change the `AzureStorage` service to `AzureBlob`  in your Active Storage config (`config/storage.yml`)
         | 
| 13 15 | 
             
            4. Restart or deploy the app.
         | 
| 14 16 |  | 
| 17 | 
            +
            Example config:
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ```
         | 
| 20 | 
            +
            microsoft:
         | 
| 21 | 
            +
              service: AzureBlob
         | 
| 22 | 
            +
              storage_account_name: account_name
         | 
| 23 | 
            +
              storage_access_key: SECRET_KEY
         | 
| 24 | 
            +
              container: container_name
         | 
| 25 | 
            +
            ```
         | 
| 26 | 
            +
             | 
| 15 27 | 
             
            ### Managed Identity (Entra ID)
         | 
| 16 28 |  | 
| 17 29 | 
             
            AzureBlob supports managed identities on :
         | 
| @@ -22,9 +34,7 @@ AzureBlob supports managed identities on : | |
| 22 34 |  | 
| 23 35 | 
             
            AKS support will likely require more work. Contributions are welcome.
         | 
| 24 36 |  | 
| 25 | 
            -
            To authenticate through managed identities instead of a shared key, omit `storage_access_key` from your `storage.yml` file | 
| 26 | 
            -
             | 
| 27 | 
            -
            It is recommended to add the identity's `principal_id` to the config.
         | 
| 37 | 
            +
            To authenticate through managed identities instead of a shared key, omit `storage_access_key` from your `storage.yml` file and pass in the identity `principal_id`.
         | 
| 28 38 |  | 
| 29 39 | 
             
            ActiveStorage config example:
         | 
| 30 40 |  | 
| @@ -79,11 +89,12 @@ A dev environment is supplied through Nix with [devenv](https://devenv.sh/). | |
| 79 89 |  | 
| 80 90 | 
             
            To test with Entra ID, the `AZURE_ACCESS_KEY` environment variable must be unset and the code must be ran or proxied through a VPS with the proper roles.
         | 
| 81 91 |  | 
| 82 | 
            -
            For cost saving, the terraform variable `create_vm`  | 
| 83 | 
            -
            To create the VPS, Create a var file `var.tfvars` containing:
         | 
| 92 | 
            +
            For cost saving, the terraform variable `create_vm` and `create_app_service` are false by default.
         | 
| 93 | 
            +
            To create the VPS and App service, Create a var file `var.tfvars` containing:
         | 
| 84 94 |  | 
| 85 95 | 
             
            ```
         | 
| 86 96 | 
             
            create_vm = true
         | 
| 97 | 
            +
            create_app_service = true
         | 
| 87 98 | 
             
            ```
         | 
| 88 99 | 
             
            and re-apply terraform: `terraform apply -var-file=var.tfvars`.
         | 
| 89 100 |  | 
    
        data/lib/azure_blob/blob.rb
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            module AzureBlob
         | 
| 4 | 
            -
              # AzureBlob::Blob holds the  | 
| 4 | 
            +
              # AzureBlob::Blob holds the metadata for a given Blob.
         | 
| 5 5 | 
             
              class Blob
         | 
| 6 6 | 
             
                # You should not instanciate this object directly,
         | 
| 7 7 | 
             
                # but obtain one when calling relevant methods of AzureBlob::Client.
         | 
| @@ -32,7 +32,7 @@ module AzureBlob | |
| 32 32 | 
             
                  response.code == "200"
         | 
| 33 33 | 
             
                end
         | 
| 34 34 |  | 
| 35 | 
            -
                # Returns the custom Azure  | 
| 35 | 
            +
                # Returns the custom Azure metadata tagged on the blob.
         | 
| 36 36 | 
             
                def metadata
         | 
| 37 37 | 
             
                  @metadata || response
         | 
| 38 38 | 
             
                    .to_hash
         | 
    
        data/lib/azure_blob/client.rb
    CHANGED
    
    | @@ -3,6 +3,8 @@ | |
| 3 3 | 
             
            require_relative "block_list"
         | 
| 4 4 | 
             
            require_relative "blob_list"
         | 
| 5 5 | 
             
            require_relative "blob"
         | 
| 6 | 
            +
            require_relative "container"
         | 
| 7 | 
            +
            require_relative "tags"
         | 
| 6 8 | 
             
            require_relative "http"
         | 
| 7 9 | 
             
            require_relative "shared_key_signer"
         | 
| 8 10 | 
             
            require_relative "entra_id_signer"
         | 
| @@ -13,13 +15,22 @@ module AzureBlob | |
| 13 15 | 
             
              # AzureBlob Client class. You interact with the Azure Blob api
         | 
| 14 16 | 
             
              # through an instance of this class.
         | 
| 15 17 | 
             
              class Client
         | 
| 16 | 
            -
                def initialize(account_name:, access_key | 
| 18 | 
            +
                def initialize(account_name:, access_key: nil, principal_id: nil, container:, **options)
         | 
| 17 19 | 
             
                  @account_name = account_name
         | 
| 18 20 | 
             
                  @container = container
         | 
| 21 | 
            +
                  @cloud_regions = options[:cloud_regions]&.to_sym || :global
         | 
| 19 22 |  | 
| 20 | 
            -
                   | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            +
                  no_access_key = access_key.nil? || access_key&.empty?
         | 
| 24 | 
            +
                  using_managed_identities = no_access_key && !principal_id.nil? || options[:use_managed_identities]
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  if !using_managed_identities && no_access_key
         | 
| 27 | 
            +
                    raise AzureBlob::Error.new(
         | 
| 28 | 
            +
                      "`access_key` cannot be empty. To use managed identities instead, pass a `principal_id` or set `use_managed_identities` to true."
         | 
| 29 | 
            +
                    )
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                  @signer = using_managed_identities ?
         | 
| 32 | 
            +
                    AzureBlob::EntraIdSigner.new(account_name:, host:, principal_id:) :
         | 
| 33 | 
            +
                    AzureBlob::SharedKeySigner.new(account_name:, access_key:)
         | 
| 23 34 | 
             
                end
         | 
| 24 35 |  | 
| 25 36 | 
             
                # Create a blob of type block. Will automatically split the the blob in multiple block and send the blob in pieces (blocks) if the blob is too big.
         | 
| @@ -138,7 +149,7 @@ module AzureBlob | |
| 138 149 | 
             
                #
         | 
| 139 150 | 
             
                # Calls to {Get Blob Properties}[https://learn.microsoft.com/en-us/rest/api/storageservices/get-blob-properties]
         | 
| 140 151 | 
             
                #
         | 
| 141 | 
            -
                # This can be used to see if the blob exist or obtain  | 
| 152 | 
            +
                # This can be used to see if the blob exist or obtain metadata such as content type, disposition, checksum or Azure custom metadata.
         | 
| 142 153 | 
             
                def get_blob_properties(key, options = {})
         | 
| 143 154 | 
             
                  uri = generate_uri("#{container}/#{key}")
         | 
| 144 155 |  | 
| @@ -147,6 +158,51 @@ module AzureBlob | |
| 147 158 | 
             
                  Blob.new(response)
         | 
| 148 159 | 
             
                end
         | 
| 149 160 |  | 
| 161 | 
            +
                # Returns the tags associated with a blob
         | 
| 162 | 
            +
                #
         | 
| 163 | 
            +
                # Calls to the {Get Blob Tags}[https://learn.microsoft.com/en-us/rest/api/storageservices/get-blob-tags] endpoint.
         | 
| 164 | 
            +
                #
         | 
| 165 | 
            +
                # Takes a key (path) of the blob.
         | 
| 166 | 
            +
                #
         | 
| 167 | 
            +
                # Returns a hash of the blob's tags.
         | 
| 168 | 
            +
                def get_blob_tags(key)
         | 
| 169 | 
            +
                  uri = generate_uri("#{container}/#{key}?comp=tags")
         | 
| 170 | 
            +
                  response = Http.new(uri, signer:).get
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                  Tags.from_response(response).to_h
         | 
| 173 | 
            +
                end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                # Returns a Container object.
         | 
| 176 | 
            +
                #
         | 
| 177 | 
            +
                # Calls to {Get Container Properties}[https://learn.microsoft.com/en-us/rest/api/storageservices/get-container-properties]
         | 
| 178 | 
            +
                #
         | 
| 179 | 
            +
                # This can be used to see if the container exist or obtain metadata.
         | 
| 180 | 
            +
                def get_container_properties(options = {})
         | 
| 181 | 
            +
                  uri = generate_uri(container)
         | 
| 182 | 
            +
                  uri.query = URI.encode_www_form(restype: "container")
         | 
| 183 | 
            +
                  response = Http.new(uri, signer:, raise_on_error: false).head
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                  Container.new(response)
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                # Create the container
         | 
| 189 | 
            +
                #
         | 
| 190 | 
            +
                # Calls to {Create Container}[https://learn.microsoft.com/en-us/rest/api/storageservices/create-container]
         | 
| 191 | 
            +
                def create_container(options = {})
         | 
| 192 | 
            +
                  uri = generate_uri(container)
         | 
| 193 | 
            +
                  uri.query = URI.encode_www_form(restype: "container")
         | 
| 194 | 
            +
                  response = Http.new(uri, signer:).put
         | 
| 195 | 
            +
                end
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                # Delete the container
         | 
| 198 | 
            +
                #
         | 
| 199 | 
            +
                # Calls to {Delete Container}[https://learn.microsoft.com/en-us/rest/api/storageservices/delete-container]
         | 
| 200 | 
            +
                def delete_container(options = {})
         | 
| 201 | 
            +
                  uri = generate_uri(container)
         | 
| 202 | 
            +
                  uri.query = URI.encode_www_form(restype: "container")
         | 
| 203 | 
            +
                  response = Http.new(uri, signer:).delete
         | 
| 204 | 
            +
                end
         | 
| 205 | 
            +
             | 
| 150 206 | 
             
                # Return a URI object to a resource in the container. Takes a path.
         | 
| 151 207 | 
             
                #
         | 
| 152 208 | 
             
                # Example: +generate_uri("#{container}/#{key}")+
         | 
| @@ -189,7 +245,7 @@ module AzureBlob | |
| 189 245 | 
             
                    "x-ms-blob-content-disposition": options[:content_disposition],
         | 
| 190 246 | 
             
                  }
         | 
| 191 247 |  | 
| 192 | 
            -
                  Http.new(uri, headers,  | 
| 248 | 
            +
                  Http.new(uri, headers, signer:, **options.slice(:metadata, :tags)).put(nil)
         | 
| 193 249 | 
             
                end
         | 
| 194 250 |  | 
| 195 251 | 
             
                # Append a block to an Append Blob
         | 
| @@ -264,7 +320,7 @@ module AzureBlob | |
| 264 320 | 
             
                    "x-ms-blob-content-disposition": options[:content_disposition],
         | 
| 265 321 | 
             
                  }
         | 
| 266 322 |  | 
| 267 | 
            -
                  Http.new(uri, headers,  | 
| 323 | 
            +
                  Http.new(uri, headers, signer:, **options.slice(:metadata, :tags)).put(content)
         | 
| 268 324 | 
             
                end
         | 
| 269 325 |  | 
| 270 326 | 
             
                private
         | 
| @@ -296,13 +352,13 @@ module AzureBlob | |
| 296 352 | 
             
                    "x-ms-blob-content-disposition": options[:content_disposition],
         | 
| 297 353 | 
             
                  }
         | 
| 298 354 |  | 
| 299 | 
            -
                  Http.new(uri, headers,  | 
| 355 | 
            +
                  Http.new(uri, headers, signer:, **options.slice(:metadata, :tags)).put(content.read)
         | 
| 300 356 | 
             
                end
         | 
| 301 357 |  | 
| 302 | 
            -
                attr_reader :account_name, :signer, :container, :http
         | 
| 303 | 
            -
             | 
| 304 358 | 
             
                def host
         | 
| 305 | 
            -
                  "https://#{account_name}.blob | 
| 359 | 
            +
                  @host ||= "https://#{account_name}.blob.#{CLOUD_REGIONS_SUFFIX[cloud_regions]}"
         | 
| 306 360 | 
             
                end
         | 
| 361 | 
            +
             | 
| 362 | 
            +
                attr_reader :account_name, :signer, :container, :http, :cloud_regions
         | 
| 307 363 | 
             
              end
         | 
| 308 364 | 
             
            end
         | 
    
        data/lib/azure_blob/const.rb
    CHANGED
    
    | @@ -2,7 +2,13 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module AzureBlob
         | 
| 4 4 | 
             
              API_VERSION = "2024-05-04"
         | 
| 5 | 
            -
              MAX_UPLOAD_SIZE = 256 * 1024 * 1024
         | 
| 6 | 
            -
              DEFAULT_BLOCK_SIZE = 128 * 1024 * 1024
         | 
| 5 | 
            +
              MAX_UPLOAD_SIZE = 256 * 1024 * 1024 # 256 Megabytes
         | 
| 6 | 
            +
              DEFAULT_BLOCK_SIZE = 128 * 1024 * 1024 # 128 Megabytes
         | 
| 7 7 | 
             
              BLOB_SERVICE = "b"
         | 
| 8 | 
            +
              CLOUD_REGIONS_SUFFIX = {
         | 
| 9 | 
            +
                global: "core.windows.net",
         | 
| 10 | 
            +
                cn: "core.chinacloudapi.cn",
         | 
| 11 | 
            +
                de: "core.cloudapi.de",
         | 
| 12 | 
            +
                usgovt: "core.usgovcloudapi.net",
         | 
| 13 | 
            +
              }
         | 
| 8 14 | 
             
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module AzureBlob
         | 
| 4 | 
            +
              # AzureBlob::Container holds the metadata for a given Container.
         | 
| 5 | 
            +
              class Container
         | 
| 6 | 
            +
                # You should not instanciate this object directly,
         | 
| 7 | 
            +
                # but obtain one when calling relevant methods of AzureBlob::Client.
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # Expects a Net::HTTPResponse object from a
         | 
| 10 | 
            +
                # HEAD or GET request to a container uri.
         | 
| 11 | 
            +
                def initialize(response)
         | 
| 12 | 
            +
                  @response = response
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
             | 
| 16 | 
            +
                def present?
         | 
| 17 | 
            +
                  response.code == "200"
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                # Returns the custom Azure metadata tagged on the container.
         | 
| 21 | 
            +
                def metadata
         | 
| 22 | 
            +
                  @metadata || response
         | 
| 23 | 
            +
                    .to_hash
         | 
| 24 | 
            +
                    .select { |key, _| key.start_with?("x-ms-meta") }
         | 
| 25 | 
            +
                    .transform_values(&:first)
         | 
| 26 | 
            +
                    .transform_keys { |key| key.delete_prefix("x-ms-meta-").to_sym }
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                private
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                attr_reader :response
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -12,10 +12,12 @@ module AzureBlob | |
| 12 12 | 
             
              class EntraIdSigner # :nodoc:
         | 
| 13 13 | 
             
                attr_reader :token
         | 
| 14 14 | 
             
                attr_reader :account_name
         | 
| 15 | 
            +
                attr_reader :host
         | 
| 15 16 |  | 
| 16 | 
            -
                def initialize(account_name:, principal_id: nil)
         | 
| 17 | 
            +
                def initialize(account_name:, host:, principal_id: nil)
         | 
| 17 18 | 
             
                  @token = AzureBlob::IdentityToken.new(principal_id:)
         | 
| 18 19 | 
             
                  @account_name = account_name
         | 
| 20 | 
            +
                  @host = host
         | 
| 19 21 | 
             
                end
         | 
| 20 22 |  | 
| 21 23 | 
             
                def authorization_header(uri:, verb:, headers: {})
         | 
    
        data/lib/azure_blob/http.rb
    CHANGED
    
    | @@ -12,7 +12,10 @@ module AzureBlob | |
| 12 12 | 
             
                  def initialize(body: nil, status: nil)
         | 
| 13 13 | 
             
                    @body = body
         | 
| 14 14 | 
             
                    @status = status
         | 
| 15 | 
            -
             | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def inspect
         | 
| 18 | 
            +
                    @body
         | 
| 16 19 | 
             
                  end
         | 
| 17 20 | 
             
                end
         | 
| 18 21 | 
             
                class FileNotFoundError < Error; end
         | 
| @@ -21,11 +24,16 @@ module AzureBlob | |
| 21 24 |  | 
| 22 25 | 
             
                include REXML
         | 
| 23 26 |  | 
| 24 | 
            -
                def initialize(uri, headers = {}, signer: nil, metadata: {}, debug: false)
         | 
| 27 | 
            +
                def initialize(uri, headers = {}, signer: nil, metadata: {}, tags: {}, debug: false, raise_on_error: true)
         | 
| 28 | 
            +
                  @raise_on_error = raise_on_error
         | 
| 25 29 | 
             
                  @date = Time.now.httpdate
         | 
| 26 30 | 
             
                  @uri = uri
         | 
| 27 31 | 
             
                  @signer = signer
         | 
| 28 | 
            -
                  @headers = headers.merge( | 
| 32 | 
            +
                  @headers = headers.merge(
         | 
| 33 | 
            +
                    Metadata.new(metadata).headers,
         | 
| 34 | 
            +
                    Tags.new(tags).headers,
         | 
| 35 | 
            +
                  )
         | 
| 36 | 
            +
             | 
| 29 37 | 
             
                  sanitize_headers
         | 
| 30 38 |  | 
| 31 39 | 
             
                  @http = Net::HTTP.new(uri.hostname, uri.port)
         | 
| @@ -42,7 +50,7 @@ module AzureBlob | |
| 42 50 | 
             
                  response.body
         | 
| 43 51 | 
             
                end
         | 
| 44 52 |  | 
| 45 | 
            -
                def put(content)
         | 
| 53 | 
            +
                def put(content = "")
         | 
| 46 54 | 
             
                  sign_request("PUT") if signer
         | 
| 47 55 | 
             
                  @response = http.start do |http|
         | 
| 48 56 | 
             
                    http.put(uri, content, headers)
         | 
| @@ -51,7 +59,7 @@ module AzureBlob | |
| 51 59 | 
             
                  true
         | 
| 52 60 | 
             
                end
         | 
| 53 61 |  | 
| 54 | 
            -
                def post(content)
         | 
| 62 | 
            +
                def post(content = "")
         | 
| 55 63 | 
             
                  sign_request("POST") if signer
         | 
| 56 64 | 
             
                  @response = http.start do |http|
         | 
| 57 65 | 
             
                    http.post(uri, content, headers)
         | 
| @@ -107,6 +115,7 @@ module AzureBlob | |
| 107 115 | 
             
                end
         | 
| 108 116 |  | 
| 109 117 | 
             
                def raise_error
         | 
| 118 | 
            +
                  return unless raise_on_error
         | 
| 110 119 | 
             
                  raise error_from_response.new(body: @response.body, status: @response.code&.to_i)
         | 
| 111 120 | 
             
                end
         | 
| 112 121 |  | 
| @@ -115,13 +124,13 @@ module AzureBlob | |
| 115 124 | 
             
                end
         | 
| 116 125 |  | 
| 117 126 | 
             
                def azure_error_code
         | 
| 118 | 
            -
                  Document.new(response.body).get_elements("//Error/Code").first.get_text.to_s
         | 
| 127 | 
            +
                  Document.new(response.body).get_elements("//Error/Code").first.get_text.to_s if response.body
         | 
| 119 128 | 
             
                end
         | 
| 120 129 |  | 
| 121 130 | 
             
                def error_from_response
         | 
| 122 131 | 
             
                  ERROR_MAPPINGS[status] || ERROR_CODE_MAPPINGS[azure_error_code] || Error
         | 
| 123 132 | 
             
                end
         | 
| 124 133 |  | 
| 125 | 
            -
                attr_accessor :host, :http, :signer, :response, :headers, :uri, :date
         | 
| 134 | 
            +
                attr_accessor :host, :http, :signer, :response, :headers, :uri, :date, :raise_on_error
         | 
| 126 135 | 
             
              end
         | 
| 127 136 | 
             
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            require "rexml/document"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module AzureBlob
         | 
| 4 | 
            +
              class Tags # :nodoc:
         | 
| 5 | 
            +
                def self.from_response(response)
         | 
| 6 | 
            +
                  document = REXML::Document.new(response)
         | 
| 7 | 
            +
                  tags = {}
         | 
| 8 | 
            +
                  document.elements.each("Tags/TagSet/Tag") do |tag|
         | 
| 9 | 
            +
                    key = tag.elements["Key"].text
         | 
| 10 | 
            +
                    value = tag.elements["Value"].text
         | 
| 11 | 
            +
                    tags[key] = value
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                  new(tags)
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def initialize(tags = nil)
         | 
| 17 | 
            +
                  @tags = tags || {}
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def headers
         | 
| 21 | 
            +
                  return {} if @tags.empty?
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  {
         | 
| 24 | 
            +
                    "x-ms-tags":
         | 
| 25 | 
            +
                    @tags.map do |key, value|
         | 
| 26 | 
            +
                      %(#{key}=#{value})
         | 
| 27 | 
            +
                    end.join("&"),
         | 
| 28 | 
            +
                  }
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def to_h
         | 
| 32 | 
            +
                  @tags
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -6,7 +6,7 @@ module AzureBlob | |
| 6 6 | 
             
                EXPIRATION_BUFFER = 3600 # 1 hours
         | 
| 7 7 | 
             
                def initialize(account_name:, signer:)
         | 
| 8 8 | 
             
                  @uri = URI.parse(
         | 
| 9 | 
            -
                    " | 
| 9 | 
            +
                    "#{signer.host}/?restype=service&comp=userdelegationkey"
         | 
| 10 10 | 
             
                  )
         | 
| 11 11 |  | 
| 12 12 | 
             
                  @signer = signer
         | 
    
        data/lib/azure_blob/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: azure-blob
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.5. | 
| 4 | 
            +
              version: 0.5.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Joé Dupuis
         | 
| @@ -44,12 +44,14 @@ files: | |
| 44 44 | 
             
            - lib/azure_blob/canonicalized_resource.rb
         | 
| 45 45 | 
             
            - lib/azure_blob/client.rb
         | 
| 46 46 | 
             
            - lib/azure_blob/const.rb
         | 
| 47 | 
            +
            - lib/azure_blob/container.rb
         | 
| 47 48 | 
             
            - lib/azure_blob/entra_id_signer.rb
         | 
| 48 49 | 
             
            - lib/azure_blob/errors.rb
         | 
| 49 50 | 
             
            - lib/azure_blob/http.rb
         | 
| 50 51 | 
             
            - lib/azure_blob/identity_token.rb
         | 
| 51 52 | 
             
            - lib/azure_blob/metadata.rb
         | 
| 52 53 | 
             
            - lib/azure_blob/shared_key_signer.rb
         | 
| 54 | 
            +
            - lib/azure_blob/tags.rb
         | 
| 53 55 | 
             
            - lib/azure_blob/user_delegation_key.rb
         | 
| 54 56 | 
             
            - lib/azure_blob/version.rb
         | 
| 55 57 | 
             
            homepage: https://github.com/testdouble/azure-blob
         | 
| @@ -78,5 +80,5 @@ requirements: [] | |
| 78 80 | 
             
            rubygems_version: 3.3.27
         | 
| 79 81 | 
             
            signing_key:
         | 
| 80 82 | 
             
            specification_version: 4
         | 
| 81 | 
            -
            summary: Azure  | 
| 83 | 
            +
            summary: Azure Blob client and Active Storage adapter
         | 
| 82 84 | 
             
            test_files: []
         |