weeblycloud 1.0.0
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 +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.md +10 -0
- data/README.md +249 -0
- data/Rakefile +10 -0
- data/bin/setup +8 -0
- data/lib/weeblycloud.rb +22 -0
- data/lib/weeblycloud/account.rb +51 -0
- data/lib/weeblycloud/blog.rb +52 -0
- data/lib/weeblycloud/blogpost.rb +28 -0
- data/lib/weeblycloud/cloudclient/cloudclient.rb +119 -0
- data/lib/weeblycloud/cloudclient/exceptions.rb +17 -0
- data/lib/weeblycloud/cloudclient/weeblycloudresponse.rb +141 -0
- data/lib/weeblycloud/cloudresource.rb +62 -0
- data/lib/weeblycloud/deleteable.rb +13 -0
- data/lib/weeblycloud/form.rb +39 -0
- data/lib/weeblycloud/formentry.rb +26 -0
- data/lib/weeblycloud/group.rb +30 -0
- data/lib/weeblycloud/member.rb +30 -0
- data/lib/weeblycloud/page.rb +38 -0
- data/lib/weeblycloud/plan.rb +27 -0
- data/lib/weeblycloud/saveable.rb +36 -0
- data/lib/weeblycloud/site.rb +194 -0
- data/lib/weeblycloud/theme.rb +26 -0
- data/lib/weeblycloud/user.rb +71 -0
- data/lib/weeblycloud/version.rb +3 -0
- data/weeblycloud.gemspec +33 -0
- metadata +128 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require "weeblycloud/cloudresource"
|
2
|
+
require "weeblycloud/saveable"
|
3
|
+
require "weeblycloud/deleteable"
|
4
|
+
|
5
|
+
module Weeblycloud
|
6
|
+
|
7
|
+
# Represents a BlogPost resource.
|
8
|
+
# https://cloud-developer.weebly.com/blog-post.html
|
9
|
+
class BlogPost < CloudResource
|
10
|
+
include Deleteable, Saveable
|
11
|
+
|
12
|
+
def initialize(user_id, site_id, blog_id, blog_post_id, data = nil)
|
13
|
+
@user_id = user_id.to_i
|
14
|
+
@site_id = site_id.to_i
|
15
|
+
@blog_id = blog_id.to_i
|
16
|
+
@blog_post_id = blog_post_id.to_i
|
17
|
+
@endpoint = "user/#{@user_id}/site/#{@site_id}/blog/#{@blog_id}/post/#{@blog_post_id}"
|
18
|
+
|
19
|
+
super(data)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the blog_post_id
|
23
|
+
def id
|
24
|
+
@blog_post_id
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require "openssl"
|
2
|
+
require "base64"
|
3
|
+
require "json"
|
4
|
+
require "http"
|
5
|
+
require "weeblycloud/cloudclient/exceptions"
|
6
|
+
|
7
|
+
module Weeblycloud
|
8
|
+
class CloudClient
|
9
|
+
@@api_key = nil
|
10
|
+
@@api_secret = nil
|
11
|
+
|
12
|
+
def initialize(api_key = nil, api_secret = nil)
|
13
|
+
if api_key || api_secret
|
14
|
+
self.configure(api_key, api_secret)
|
15
|
+
elsif @@api_key.nil? || @@api_secret.nil?
|
16
|
+
raise "No API keys provided."
|
17
|
+
end
|
18
|
+
|
19
|
+
@BASE_API = "https://api.weeblycloud.com/"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Globally configure API key and secret
|
23
|
+
def self.configure(api_key, api_secret)
|
24
|
+
@@api_key = api_key
|
25
|
+
@@api_secret = api_secret
|
26
|
+
end
|
27
|
+
|
28
|
+
# Make a GET request
|
29
|
+
def get(endpoint, options={})
|
30
|
+
ops = {
|
31
|
+
:page_size => nil,
|
32
|
+
:page => nil,
|
33
|
+
:params => {},
|
34
|
+
:content => {}
|
35
|
+
}
|
36
|
+
ops.merge!(options)
|
37
|
+
if ops[:page_size]
|
38
|
+
ops[:params].merge!({"page_size" => ops[:page_size]})
|
39
|
+
end
|
40
|
+
|
41
|
+
if ops[:page]
|
42
|
+
ops[:params].merge!({"page" => options[:page]})
|
43
|
+
end
|
44
|
+
|
45
|
+
return call("GET", endpoint, ops[:content], ops[:params])
|
46
|
+
end
|
47
|
+
|
48
|
+
# Make a POST request
|
49
|
+
def post(endpoint, options={})
|
50
|
+
ops = {:params => {}, :content => {}}
|
51
|
+
ops.merge!(options)
|
52
|
+
|
53
|
+
return call("POST", endpoint, ops[:content], ops[:params])
|
54
|
+
end
|
55
|
+
|
56
|
+
# Make a PATCH request
|
57
|
+
def patch(endpoint, options={})
|
58
|
+
ops = {:params => {}, :content => {}}
|
59
|
+
ops.merge!(options)
|
60
|
+
|
61
|
+
return call("PATCH", endpoint, ops[:content], ops[:params])
|
62
|
+
end
|
63
|
+
|
64
|
+
# Make a PUT request
|
65
|
+
def put(endpoint, options={})
|
66
|
+
ops = {:params => {}, :content => {}}
|
67
|
+
ops.merge!(options)
|
68
|
+
|
69
|
+
return call("PUT", endpoint, ops[:content], ops[:params])
|
70
|
+
end
|
71
|
+
|
72
|
+
# Make a DELETE request
|
73
|
+
def delete(endpoint, options={})
|
74
|
+
ops = {:params => {}, :content => {}}
|
75
|
+
ops.merge!(options)
|
76
|
+
|
77
|
+
return call("DELETE", endpoint, ops[:content], ops[:params])
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Make an API call
|
83
|
+
def call(method, endpoint, content={}, params={})
|
84
|
+
json_data = content.to_json
|
85
|
+
strip_slashes(endpoint)
|
86
|
+
headers = {
|
87
|
+
"Content-Type" => "application/json",
|
88
|
+
"X-Cloud-Client-Type" => "ruby",
|
89
|
+
"X-Cloud-Client-Version" => VERSION,
|
90
|
+
"X-Public-Key" => @@api_key,
|
91
|
+
"X-Signed-Request-Hash" => sign(method, endpoint, json_data)
|
92
|
+
}
|
93
|
+
url = @BASE_API + endpoint
|
94
|
+
response = HTTP.headers(headers).request(method, url, :body => json_data, :params => params)
|
95
|
+
return WeeblyCloudResponse.new(response, url, headers, content, params)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Signs a request and returns the HMAC hash.
|
99
|
+
# See https://cloud-developer.weebly.com/about-the-rest-apis.html#signing-and-authenticating-requests
|
100
|
+
def sign(request_type, endpoint, content)
|
101
|
+
request = request_type + "\n" + endpoint + "\n" + content
|
102
|
+
digest = OpenSSL::Digest.new("sha256")
|
103
|
+
mac = OpenSSL::HMAC.hexdigest(digest, @@api_secret, request)
|
104
|
+
base = Base64.strict_encode64(mac)
|
105
|
+
return base
|
106
|
+
end
|
107
|
+
|
108
|
+
# Remove a "/" if it is the first or last character in a string.
|
109
|
+
def strip_slashes(str)
|
110
|
+
if str.index("/") === 0
|
111
|
+
str = str.slice(1..-1)
|
112
|
+
end
|
113
|
+
if str.index("/") === str.length
|
114
|
+
str = str.slice(0..-2)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Weeblycloud
|
2
|
+
|
3
|
+
# Raised with a response error from the API
|
4
|
+
class ResponseError < StandardError
|
5
|
+
attr_reader :code, :message
|
6
|
+
|
7
|
+
def initialize(msg = "Unknown error occured", code)
|
8
|
+
@code = code
|
9
|
+
@message = msg
|
10
|
+
m = "(CODE: \##{@code}) #{@msg}"
|
11
|
+
super(m)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Raised with invalid pagination method invocation
|
16
|
+
PaginationError = Class.new(StandardError)
|
17
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require "HTTP"
|
2
|
+
require "json"
|
3
|
+
require "weeblycloud/cloudclient/exceptions"
|
4
|
+
|
5
|
+
module Weeblycloud
|
6
|
+
|
7
|
+
class WeeblyCloudResponse
|
8
|
+
include Enumerable
|
9
|
+
attr_reader :json, :page_size, :max_page, :current_page, :records, :list
|
10
|
+
|
11
|
+
def initialize(response, endpoint, headers, content, params)
|
12
|
+
@page_size = nil
|
13
|
+
@max_page = nil
|
14
|
+
@current_page = nil
|
15
|
+
@records = nil
|
16
|
+
@is_paginated = false
|
17
|
+
|
18
|
+
# private
|
19
|
+
@response = response
|
20
|
+
@endpoint = endpoint
|
21
|
+
@content = content
|
22
|
+
@params = params
|
23
|
+
@headers = headers
|
24
|
+
@first_iter = true
|
25
|
+
|
26
|
+
refresh()
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns whether the results are paginated
|
30
|
+
def paginated?
|
31
|
+
@is_paginated
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get the next page, return True on success, False on failure. If the WeeblyCloudResponse is
|
35
|
+
# not paginated, raise an exception.
|
36
|
+
def next_page()
|
37
|
+
if not @is_paginated
|
38
|
+
raise PaginationError, "Not paginated"
|
39
|
+
end
|
40
|
+
|
41
|
+
if @current_page >= @max_page
|
42
|
+
return False
|
43
|
+
end
|
44
|
+
|
45
|
+
next_page = @params["page"].nil? ? 2 : @params["page"] + 1
|
46
|
+
|
47
|
+
@params.merge!({"page"=>next_page})
|
48
|
+
@response = HTTP.headers(@headers).get(@endpoint, :body => "{}", :params => @params)
|
49
|
+
refresh()
|
50
|
+
end
|
51
|
+
|
52
|
+
# Iterate over all pages. Accepts a block.
|
53
|
+
def each(&block)
|
54
|
+
if @is_paginated
|
55
|
+
# loop over all pages
|
56
|
+
while @current_page < @max_page
|
57
|
+
@list.each{|item| yield(item) }
|
58
|
+
if @first_iter
|
59
|
+
@first_iter = false
|
60
|
+
else
|
61
|
+
next_page()
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
# If it isn't paginated just do it once
|
66
|
+
@list.each{ |item| yield(item) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the current page as a JSON string
|
71
|
+
def to_s()
|
72
|
+
@json.to_json
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the current page as a hash
|
76
|
+
def to_hash()
|
77
|
+
@json
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Refreshes the internal parameters based on the current value of @request.
|
83
|
+
def refresh()
|
84
|
+
@status_code = @response.code
|
85
|
+
|
86
|
+
# Handle errors. Sometimes there may not be a response body (which is why ValueError)
|
87
|
+
# must be caught.
|
88
|
+
begin
|
89
|
+
if !([200,204].include? @status_code) || @response.parse().include?("error")
|
90
|
+
error = @response.parse()
|
91
|
+
raise ResponseError.new(error["error"]["message"], error["error"]["code"])
|
92
|
+
end
|
93
|
+
rescue NoMethodError, HTTP::Error
|
94
|
+
# Sometimes DELETE returns nothing. When this is the case, it will have a status code 204
|
95
|
+
raise ResponseError.new(code=@status_code) unless @status_code == 204
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get information on paging if response is paginated
|
99
|
+
headers = @response.headers
|
100
|
+
if headers["X-Resultset-Total"] && headers["X-Resultset-Total"].to_i > headers["X-Resultset-Limit"].to_i
|
101
|
+
@is_paginated = true
|
102
|
+
@records = headers["X-Resultset-Total"].to_i
|
103
|
+
@page_size = headers["X-Resultset-Limit"].to_i
|
104
|
+
@current_page = headers["X-Resultset-Page"].to_i
|
105
|
+
@max_page = (@records.to_f / @page_size.to_i).ceil
|
106
|
+
end
|
107
|
+
|
108
|
+
# Save the content of the request
|
109
|
+
begin
|
110
|
+
@json = @response.parse()
|
111
|
+
|
112
|
+
# Parse the result so it can be paged over.
|
113
|
+
if @is_paginated
|
114
|
+
if @json.is_a?(Hash) == true
|
115
|
+
@list = @json.first[1]
|
116
|
+
else
|
117
|
+
@list = @json
|
118
|
+
end
|
119
|
+
else
|
120
|
+
if @json.kind_of?(Array)
|
121
|
+
@list = @json
|
122
|
+
elsif @json.values[0].kind_of?(Array)
|
123
|
+
@list = @json.values[0]
|
124
|
+
else
|
125
|
+
# I think this accounts for plan
|
126
|
+
@list = @json.values
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
rescue NoMethodError, HTTP::Error
|
131
|
+
# Sometimes DELETE returns nothing. When this is the case, it will have a status code 204
|
132
|
+
if @status_code == 204
|
133
|
+
@json = {"success" => true}
|
134
|
+
else
|
135
|
+
raise ResponseError.new(code = @status_code)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'weeblycloud/cloudclient/cloudclient'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Weeblycloud
|
5
|
+
|
6
|
+
# A base resource that all other resources inherit from.
|
7
|
+
class CloudResource
|
8
|
+
attr_reader :properties
|
9
|
+
|
10
|
+
def initialize(data = nil)
|
11
|
+
@client = CloudClient.new
|
12
|
+
@properties = {}
|
13
|
+
@changed = {}
|
14
|
+
|
15
|
+
# If data isn't provided, make an API call to get it
|
16
|
+
if data
|
17
|
+
@properties = data
|
18
|
+
@got = true
|
19
|
+
else
|
20
|
+
get()
|
21
|
+
@got = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get a property. Returns nil if the property does not exist.
|
26
|
+
def get_property(prop)
|
27
|
+
begin
|
28
|
+
return @properties.fetch(prop)
|
29
|
+
raise KeyError
|
30
|
+
if @got
|
31
|
+
return nil
|
32
|
+
else
|
33
|
+
get()
|
34
|
+
@got = true
|
35
|
+
return get_property(prop)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get a property. Returns nil if the property does not exist.
|
41
|
+
def [](prop)
|
42
|
+
get_property(prop)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the properties as a json string
|
46
|
+
def to_s
|
47
|
+
@properties.to_json
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the ID for the resource object
|
51
|
+
def id
|
52
|
+
raise "Method not implemented."
|
53
|
+
end
|
54
|
+
|
55
|
+
# Gets the resources with an API call
|
56
|
+
def get
|
57
|
+
@response = @client.get(@endpoint)
|
58
|
+
@properties = @response.json
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "weeblycloud/cloudresource"
|
2
|
+
|
3
|
+
require "weeblycloud/formentry"
|
4
|
+
|
5
|
+
module Weeblycloud
|
6
|
+
|
7
|
+
# Represents a Form resource.
|
8
|
+
# https://cloud-developer.weebly.com/form.html
|
9
|
+
class Form < CloudResource
|
10
|
+
|
11
|
+
def initialize(user_id, site_id, form_id, data = nil)
|
12
|
+
@user_id = user_id.to_i
|
13
|
+
@site_id = site_id.to_i
|
14
|
+
@form_id = form_id.to_i
|
15
|
+
|
16
|
+
@endpoint = "user/#{@user_id}/site/#{@site_id}/form/#{@form_id}"
|
17
|
+
|
18
|
+
super(data)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the form_id
|
22
|
+
def id
|
23
|
+
@form_id
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a list of FormEntry resources for a given form subject to filters.
|
27
|
+
def list_form_entries(filters={})
|
28
|
+
result = @client.get(@endpoint + "/entry", :params=>filters)
|
29
|
+
return result.map { |i| FormEntry.new(@user_id, @site_id, i["form_entry_id"], i) }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return the FormEntry with the given id.
|
33
|
+
def get_form_entry(form_entry_id)
|
34
|
+
return FormEntry.new(@user_id, @site_id, @form_id, @form_entry_id)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "weeblycloud/cloudresource"
|
2
|
+
|
3
|
+
module Weeblycloud
|
4
|
+
|
5
|
+
# Represents a FormEntry resource.
|
6
|
+
# https://cloud-developer.weebly.com/form-entry.html
|
7
|
+
class FormEntry < CloudResource
|
8
|
+
|
9
|
+
def initialize(user_id, site_id, form_id, form_entry_id, data = nil)
|
10
|
+
@user_id = user_id.to_i
|
11
|
+
@site_id = site_id.to_i
|
12
|
+
@form_id = form_id.to_i
|
13
|
+
@form_entry_id = form_entry_id.to_i
|
14
|
+
@endpoint = "user/#{@user_id}/site/#{@site_id}/form/#{@form_id}/entry/#{@form_entry_id}"
|
15
|
+
|
16
|
+
super(data)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the form_entry_id
|
20
|
+
def id
|
21
|
+
@form_entry_id
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|