Class: CDNConnect::APIClient

Inherits:
Object
  • Object
show all
Defined in:
lib/cdnconnect_api.rb

Overview

Used to easily interact with CDN Connect API.

Constant Summary collapse

@@application_name =
'cdnconnect-api-ruby'
@@application_version =
'0.4.2'
@@user_agent =
@@application_name + ' v' + @@application_version
@@api_host =
'https://api.cdnconnect.com'
@@api_version =
'v1'

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ APIClient

Creates a client to authorize interactions with the API using the OAuth 2.0 protocol. This can be given a known API Key (access_token) or can use OAuth 2.0 web application flow designed for 3rd party web sites (clients).

Parameters:

  • options (Hash) (defaults to: {})

    The configuration parameters for the client.

    • :api_key - An API Key (commonly known as an access_token) which was previously created within CDN Connect’s account for a specific app.

    • :client_id - A unique identifier issued to the client to identify itself to CDN Connect’s authorization server. This is issued by CDN Connect to external clients. This is only needed if an API Key isn’t already known.

    • :client_secret - A secret issued by the CDN Connect’s authorization server, which is used to authenticate the client. Do not confuse this is an access_token or an api_key. This is only required if an API Key isn’t already known. A client secret should not be shared.

    • :scope - The scope of the access request, expressed either as an Array or as a space-delimited String.

    • :state - An unguessable random string designed to allow the client to maintain state to protect against cross-site request forgery attacks.

    • :code - The authorization code received from the authorization server.

    • :redirect_uri - The redirection URI used in the initial request.

    • :access_token - The current access token for this client, also known as the API Key. access_token and api_key options are interchangeable.

    • :api_key - The current access token for this client, also known as the access token. access_token and api_key options are interchangeable.

    • :app_host - The CDN Connect App host. For example, demo.cdnconnect.com is a CDN Connect app host. The app host should not include https://, http:// or a URL path.

    • :log_device - Ruby Logger logdev argument in Logger.new(logdev). Defaults to STDOUT.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/cdnconnect_api.rb', line 72

def initialize(options={})
    # Normalize key to String to allow indifferent access.
    options = options.inject({}) { |accu, (k, v)| accu[k.to_s] = v; accu }

    # Initialize all of the options
    @client_id = options["client_id"]
    @client_secret = options["client_secret"]
    @scope = options["scope"]
    @state = options["state"]
    @code = options["code"]
    @redirect_uri = options["redirect_uri"]
    options["access_token"] = options["access_token"] || options["api_key"] # both work
    @access_token = options["access_token"]
    @app_host = options["app_host"]
    @@api_host = options["api_host"] || @@api_host
    @prefetched_upload_urls = {}
    @upload_queue = {}
    @failed_uploads = []

    log_device = options["log_device"] || STDOUT
    @logger = Logger.new(log_device, 10, 1024000)
    @logger.sev_threshold = options["log_sev_threshold"] || Logger::WARN

    if options["api_key"] != nil and options["app_host"] == nil
        err_msg = 'app_host option required when using api_key option'
        @logger.error(err_msg)
        raise ArgumentError, err_msg
    end

    @logger.debug("#{@@user_agent}, #{@@api_host}")

    # Create the OAuth2 client which will be used to authorize the requests
    @client = Signet::OAuth2::Client.new(:client_id => client_id,
                                         :client_secret => @client_secret,
                                         :scope => @scope,
                                         :state => @state,
                                         :code => @code,
                                         :redirect_uri => @redirect_uri,
                                         :access_token => @access_token)
    return self
end

Instance Method Details

#access_tokenString

OAuth2 value. An API Key (commonly known as an access_token) which was previously created within CDN Connect’s account for a specific app.

Returns:

  • (String)


901
902
903
# File 'lib/cdnconnect_api.rb', line 901

def access_token
    @access_token
end

#app_hostString

The CDN Connect App host. For example, demo.cdnconnect.com is a CDN Connect app host. The app host value should not include https://, http:// or a URL path.

Returns:

  • (String)


911
912
913
# File 'lib/cdnconnect_api.rb', line 911

def app_host
    @app_host
end

#client_idString

OAuth2 parameter. A unique identifier issued to the client to identify itself to CDN Connect’s authorization server. This is issued by CDN Connect to external clients. This is only needed if an API Key isn’t already known.

Returns:

  • (String)


841
842
843
# File 'lib/cdnconnect_api.rb', line 841

def client_id
    @client_id
end

#client_secretString

OAuth2 parameter. A secret issued by the CDN Connect’s authorization server, which is used to authenticate the client. Do not confuse this is an access_token or an api_key. This is only required if an API Key isn’t already known. A client secret should not be shared.

Returns:

  • (String)


853
854
855
# File 'lib/cdnconnect_api.rb', line 853

def client_secret
    @client_secret
end

#codeString

OAuth2 value. The authorization code received from the authorization server.

Returns:

  • (String)


883
884
885
# File 'lib/cdnconnect_api.rb', line 883

def code
    @code
end

#create_app(options) ⇒ APIResponse

Create a new CDN Connect app. The creator of the app will be the same user as the creator of the API Key for the request.

Parameters:

  • options (Hash)

    The configuration parameters for the client.

    • :subdomain - This new app’s subdomain to the “cdnconnect.com” domain.

    • :label - The label for this new app.

Returns:

  • (APIResponse)

    A response object with helper methods to read the response.



720
721
722
723
724
725
726
# File 'lib/cdnconnect_api.rb', line 720

def create_app(options)
    path = '/apps.json'
    data = {}
    data[:subdomain] = options[:subdomain]
    data[:label] = options[:label]
    post(path, data)
end

#create_path(options) ⇒ APIResponse

Create a folder path. If any of the folders within the given path do not already exist they will be created.

Returns:

  • (APIResponse)

    A response object with helper methods to read the response.



699
700
701
702
703
704
705
# File 'lib/cdnconnect_api.rb', line 699

def create_path(options)
    if options[:app_host] == nil
        options[:app_host] = @app_host
    end
    path = "/#{options[:app_host]}#{options[:path]}/create-path.json"
    get(path)
end

#delete(path) ⇒ APIResponse

Executes a DELETE request to an API URL and returns a response object. DELETE requests are used when (you guessed it) deleting data.

Parameters:

  • path (String)

    The API path to send the DELETE request to.

Returns:

  • (APIResponse)

    A response object with helper methods to read the response.



771
772
773
# File 'lib/cdnconnect_api.rb', line 771

def delete(path)
    fetch(:path => path, :method => 'DELETE')
end

#delete_object(options) ⇒ APIResponse

Delete object info, which can be either a file or folder.

Parameters:

  • options (Hash)
    • :path - The path to the CDN Connect object to delete. (required)

Returns:

  • (APIResponse)

    A response object with helper methods to read the response.



685
686
687
688
689
690
691
# File 'lib/cdnconnect_api.rb', line 685

def delete_object(options)
    if options[:app_host] == nil
        options[:app_host] = @app_host
    end
    path = "/#{options[:app_host]}#{options[:path]}.json"
    delete(path)
end

#failed_uploadsArray

An array of files which failed.

Returns:

  • (Array)


929
930
931
# File 'lib/cdnconnect_api.rb', line 929

def failed_uploads
    @failed_uploads
end

#get(path, data = {}) ⇒ APIResponse

Executes a GET request to an API URL and returns a response object. GET requests are used when reading data.

Parameters:

  • path (String)

    The API path to send the GET request to.

  • data (Hash) (defaults to: {})

    Data which will be placed in the GET request’s querystring. (Optional)

Returns:

  • (APIResponse)

    A response object with helper methods to read the response.



736
737
738
# File 'lib/cdnconnect_api.rb', line 736

def get(path, data={})
    fetch(:path => path, :method => 'GET', :data => data)
end

#get_object(options) ⇒ APIResponse

Get object info, which can be either a file or folder.

Parameters:

  • options (Hash)
    • :path - The path to the CDN Connect object to get. (required)

    • :files - True or false value indicating if a folder’s response should contain its sub-files or not. Default is false.

    • :folders - True or false value indicating if a folder’s response should contain its sub-folders or not. Default is false.

Returns:

  • (APIResponse)

    A response object with helper methods to read the response.



643
644
645
646
647
648
649
650
651
652
653
654
655
656
# File 'lib/cdnconnect_api.rb', line 643

def get_object(options)
    if options[:app_host] == nil
        options[:app_host] = @app_host
    end
    path = "/#{options[:app_host]}#{options[:path]}.json"
    data = {}
    if options[:files] == true
        data[:files] = true
    end
    if options[:folders] == true
        data[:folders] = true
    end
    get(path, data)
end

#human_file_size(bytes) ⇒ Object



933
934
935
936
937
938
939
940
941
942
# File 'lib/cdnconnect_api.rb', line 933

def human_file_size(bytes)
    begin
        units = %w{B KB MB GB TB}
        e = (Math.log(bytes)/Math.log(1024)).floor
        s = "%.2f" % (bytes.to_f / 1024**e)
        s.sub(/\.?0*$/, units[e])
    rescue
        bytes
    end
end

#post(path, data) ⇒ APIResponse

Executes a POST request to an API URL and returns a response object. POST requests are used when creating data.

Parameters:

  • path (String)

    The API path to send the POST request to.

  • data (Hash)

    Data which will be sent in the POST request.

Returns:

  • (APIResponse)

    A response object with helper methods to read the response.



748
749
750
# File 'lib/cdnconnect_api.rb', line 748

def post(path, data)
    fetch(:path => path, :method => 'POST', :data => data)
end

#put(path, data) ⇒ APIResponse

Executes a PUT request to an API URL and returns a response object. PUT requests are used when updating data.

Parameters:

  • path (String)

    The API path to send the PUT request to.

  • data (Hash)

    Data which will be sent in the PUT request.

Returns:

  • (APIResponse)

    A response object with helper methods to read the response.



760
761
762
# File 'lib/cdnconnect_api.rb', line 760

def put(path, data)
    fetch(:path => path, :method => 'PUT', :data => data)
end

#redirect_uriString

OAuth2 value. The redirection URI used in the initial request.

Returns:

  • (String)


891
892
893
# File 'lib/cdnconnect_api.rb', line 891

def redirect_uri
    @redirect_uri
end

#rename_object(options) ⇒ APIResponse

Rename object, which can be either a file or folder.

Parameters:

  • options (Hash)
    • :path - The path to the CDN Connect object to get. (required)

    • :new_name - The new filename or folder name for the object. (required)

Returns:

  • (APIResponse)

    A response object with helper methods to read the response.



668
669
670
671
672
673
674
675
# File 'lib/cdnconnect_api.rb', line 668

def rename_object(options)
    if options[:app_host] == nil
        options[:app_host] = @app_host
    end
    path = "/#{options[:app_host]}#{options[:path]}/rename.json"
    data = { :new_name => options[:new_name] }
    put(path, data)
end

#scopeString

OAuth2 parameter. The scope of the access request, expressed either as an Array or as a space-delimited String. This is only required if an API Key isn’t already known.

Returns:

  • (String)


864
865
866
# File 'lib/cdnconnect_api.rb', line 864

def scope
    @scope
end

#stateString

OAuth2 parameter. An unguessable random string designed to allow the client to maintain state to protect against cross-site request forgery attacks. This is only required if an API Key isn’t already known.

Returns:

  • (String)


875
876
877
# File 'lib/cdnconnect_api.rb', line 875

def state
    @state
end

#upload(options) ⇒ APIResponse

Upload a file or multiple files from a local machine to a folder within a CDN Connect app. The upload method provides numerous ways to upload files or files, to include recursively drilling down through local folders and uploading only files that match your chosen extensions. If any of the folders within the upload path do not already exist then they will be created automatically.

Parameters:

  • options (Hash)

    The configuration parameters for the client.

    • :destination_path - The path of the CDN Connect folder to upload to. If the destination folder does not already exist it will automatically be created.

    • :source_file_path - A string of a source file’s local path to upload to the destination folder. If you have more than one file to upload it’d be better to use ‘source_file_paths` or `source_folder_path` instead.

    • :source_file_paths - A list of a source file’s local paths to upload. This option uploads all of the files to the destination folder. If you want to upload files in a local folder then ‘source_folder_path` option may would be easier than listing out files manually.

    • :source_folder_path - A string of a source folder’s local path to upload. This will upload all of the files in this source folder to the destination url. By using the ‘valid_extensions` parameter you can also restrict which files should be uploaded according to extension.

    • :valid_extensions - An array of valid extensions which should be uploaded. This is only applied when the ‘source_folder_path` options is used. If nothing is provided, which is the default, all files within the folder are uploaded. The extensions should be in all lower case, and they should not contain a period or asterisks. Example `valid_extensions` array => [’js’, ‘css’, ‘jpg’, jpeg’, ‘png’, ‘gif’, ‘webp’]

    • :recursive_local_folders - A true or false value indicating if this call should recursively upload all of the local folder’s sub-folders, and their sub-folders, etc. This option is only used when the ‘source_folder_path` option is used. Default is true.

    • :destination_file_name - The name which the uploaded file should be renamed to. By default the file name will be the same as the file being uploaded. The ‘destination_file_name` option is only used for a single file upload, it does not work for multiple file requests.

    • queue_processing - A true or false value indicating if the processing of the data should be queued or processed immediately. A response with “queue_processing” will be faster because the resposne doesn’t wait on the system to complete processing the data. However, because an queued processing response does not wait for the data to complete processing then the response will not contain any information about the data which was just uploaded. Use queued processing only if you do not need to know the details of the upload. Additionally you can use the ‘webhook_url` to post back the uploads details once it’s processed. Default is true.

    • :webhook_url - A URL which the system should ‘POST` the response to. This works for both immediate processing or queued processing calls. The data sent to the `webhook_url` will be the same as the data that is responded in a synchronous response, and is sent within the `data` parameter. The format sent can be in either `json` or `xml` by using the `webhook_format` parameter. By default there is no webhook URL.

    • :webhook_format - When a ‘webhook_url` is provided, you can have the data formatted as either `json` or `xml`. The defautl format is `json`.

Returns:

  • (APIResponse)

    A response object with helper methods to read the response.



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/cdnconnect_api.rb', line 174

def upload(options)
    if options[:app_host] == nil
        options[:app_host] = @app_host
    end

    # Make sure we've got good source data before starting the upload
    prepare_upload(options)

    # Place all of the source files in an upload queue for each destination folder.
    # Up to 25 files can be sent in one POST request. As uploads are successful
    # the files will be removed from the queue and uploading will stop when
    # each directory's upload queue is empty.
    build_upload_queue(options)
    
    # The returning response object. Its empty to start with then as 
    # uploads complete it fills this up with each upload's response info
    api_response = CDNConnect::APIResponse.new()
    
    # If there are files in the upload_queue then start the upload process
    while @upload_queue.length > 0
        
        # Get the destination_path in the list of upload queues
        destination_path = @upload_queue.keys[0]
        @logger.debug("destination_path: #{destination_path}")
  
        # Check if we have a prefetched upload url before requesting a new one
        upload_url = get_prefetched_upload_url(destination_path)
        if upload_url == nil
            # We do not already have an upload url created. The first upload request
            # will need to make a request for an upload url. After the first upload
            # each upload response will also include a new upload url which can be used 
            # for the next upload when uploading to the same folder.
            upload_url_response = self.get_upload_url(options, destination_path)
            if upload_url_response.is_error
                return upload_url_response
            end
            upload_url = upload_url_response.get_result('upload_url')
            upload_id = upload_url_response.get_result('upload_id')
            @logger.debug("Received new upload url, #{upload_id}")
        else
            @logger.debug("Use prefetched upload url")
        end

        # Build the data that gets sent in the POST request
        post_data = build_post_data(:destination_path => destination_path,
                                    :destination_file_name => options[:destination_file_name],
                                    :queue_processing => options.fetch(:queue_processing, true),
                                    :create_upload_url => options.fetch(:create_upload_url, true),
                                    :webhook_url => options[:webhook_url],
                                    :webhook_format => options[:webhook_format])

        # Build the request to send to the API
        # Uses the Faraday: https://github.com/lostisland/faraday
        conn = Faraday.new() do |req|
          req.headers = {
            'User-Agent' => @@user_agent
          }
          req.request :multipart
          req.adapter :net_http
        end

        # Kick off the request!
        http_response = conn.post upload_url, post_data

        # w00t! Convert the http response into APIResponse and see what's up
        upload_response = APIResponse.new(http_response)
        for msg in upload_response.msgs
            @logger.info("Upload " + msg["status"] + ": " + msg["text"])
        end

        # merge the two together so we build one awesome response
        # object with everything you need to know about every upload
        api_response.merge(upload_response)
        
        # Read the response and see what we got
        if upload_response.is_server_error
            # There was a server error, empty the active upload queue
            failed_upload_attempt(destination_path)
            @logger.error(upload_response.body)

            # put the upload url back in the list
            # of prefetched urls so it can be reused
            set_prefetched_upload_url(destination_path, upload_url)
        else
            # successful upload, clear out the active upload queue
            # and remove uploaded files from the upload queue
            successful_upload_attempt(destination_path)
            
            @logger.info("Successful upload")

            # an upload response also contains a new upload url. 
            # Save it for the next upload to the same destination.
            new_upload_url = upload_response.get_result('upload_url')
            new_upload_id = upload_response.get_result('upload_url_upload_id')
            if new_upload_url != nil
                @logger.debug("Received upload url in upload response, #{new_upload_id}")
                set_prefetched_upload_url(destination_path,
                                          new_upload_url)
            end
        end
  
    end 
    
    return api_response
end