community-zero 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,112 @@
1
+ #
2
+ # Copyright 2013, Seth Vargo <sethvargo@gmail.com>
3
+ # Copyright 2013, Opscode, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module CommunityZero
19
+ # The base class for any endpoint.
20
+ #
21
+ # @author Seth Vargo <sethvargo@gmail.com>
22
+ class Endpoint
23
+ require 'community_zero/endpoints/cookbook_endpoint'
24
+ require 'community_zero/endpoints/cookbook_versions_version_endpoint'
25
+ require 'community_zero/endpoints/cookbooks_endpoint'
26
+ require 'community_zero/endpoints/not_found_endpoint'
27
+ require 'community_zero/endpoints/search_endpoint'
28
+
29
+ METHODS = [:get, :put, :post, :delete].freeze
30
+
31
+ attr_reader :server
32
+
33
+ # Create a new endpoint.
34
+ #
35
+ # @param [CommunityZero::Server] server
36
+ # the server to respond to this endpoint
37
+ def initialize(server)
38
+ @server = server
39
+ end
40
+
41
+ # Generate the URL for the given cookbook.
42
+ #
43
+ # @param [CommunityZero::Cookbook] cookbook
44
+ # the coookbook to generate the URL for
45
+ #
46
+ # @return [String]
47
+ # the URL
48
+ def url_for(cookbook)
49
+ "#{server.url}/cookbooks/#{cookbook.name}"
50
+ end
51
+
52
+ # Generate the version URL for the given cookbook and version.
53
+ #
54
+ # @param [CommunityZero::Cookbook] cookbook
55
+ # the coookbook to generate the URL for
56
+ # @param [String] version
57
+ # the version to generate a string for
58
+ #
59
+ # @return [String]
60
+ # the URL
61
+ def version_url_for(cookbook, version)
62
+ "#{server.url}/cookbooks/#{cookbook.name}/versions/#{version.gsub('.', '_')}"
63
+ end
64
+
65
+ # Call the request.
66
+ #
67
+ # @param [CommunityZero::Request] request
68
+ # the request object
69
+ def call(request)
70
+ m = request.method.downcase.to_sym
71
+
72
+ # Only respond to listed methods
73
+ unless respond_to?(m)
74
+ allowed = METHODS.select { |m| respond_to?(m) }.map(&:upcase).join(', ')
75
+ return [
76
+ 405,
77
+ { 'Content-Type' => 'text/plain', 'Allow' => allowed },
78
+ "Method not allowed: '#{request.env['REQUEST_METHOD']}'"
79
+ ]
80
+ end
81
+
82
+ # Must accept JSON
83
+ unless request.env['HTTP_ACCEPT'].to_s.split(';').include?('application/json')
84
+ return [
85
+ 406,
86
+ { 'Content-Type' => 'text/plain' },
87
+ 'Must accept application/json'
88
+ ]
89
+ end
90
+
91
+ begin
92
+ send(m, request)
93
+ rescue RestError => e
94
+ error(e.response_code, e.error)
95
+ end
96
+ end
97
+
98
+ private
99
+ def error(response_code, error)
100
+ respond(response_code, { 'error' => error })
101
+ end
102
+
103
+ def respond(response_code = 200, content)
104
+ [
105
+ response_code,
106
+ { 'Content-Type' => 'application/json' },
107
+ JSON.pretty_generate(content)
108
+ ]
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,68 @@
1
+ #
2
+ # Copyright 2013, Seth Vargo <sethvargo@gmail.com>
3
+ # Copyright 2013, Opscode, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module CommunityZero
19
+ # The endpoint for interacting with a single cookbook.
20
+ #
21
+ # @author Seth Vargo <sethvargo@gmail.com>
22
+ class CookbookEndpoint < Endpoint
23
+ # GET /cookbooks/:name
24
+ def get(request)
25
+ name = request.path.last
26
+ cookbook = Store.find(name)
27
+
28
+ if cookbook = Store.find(name)
29
+ respond({
30
+ 'name' => cookbook.name,
31
+ 'maintainer' => cookbook.maintainer,
32
+ 'category' => cookbook.category,
33
+ 'external_url' => cookbook.external_url,
34
+ 'description' => cookbook.description,
35
+ 'average_rating' => cookbook.average_rating,
36
+ 'versions' => cookbook.versions.map { |i| version_url_for(cookbook, i) },
37
+ 'latest_version' => version_url_for(cookbook, cookbook.latest_version),
38
+ 'created_at' => cookbook.created_at,
39
+ 'updated_at' => cookbook.upadated_at,
40
+ })
41
+ else
42
+ respond(404,
43
+ {
44
+ 'error_code' => 'NOT_FOUND',
45
+ 'error_messages' => ['Resource not found'],
46
+ }
47
+ )
48
+ end
49
+ end
50
+
51
+ # DELETE /cookbooks/:name
52
+ def delete(request)
53
+ name = request.path.last
54
+
55
+ if cookbook = Store.find(name)
56
+ cookbook.destroy
57
+ respond({})
58
+ else
59
+ respond(404,
60
+ {
61
+ 'error_code' => 'NOT_FOUND',
62
+ 'error_messages' => ['Resource not found'],
63
+ }
64
+ )
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,59 @@
1
+ #
2
+ # Copyright 2013, Seth Vargo <sethvargo@gmail.com>
3
+ # Copyright 2013, Opscode, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module CommunityZero
19
+ # The endpoint for interacting with a single cookbook version.
20
+ #
21
+ # @author Seth Vargo <sethvargo@gmail.com>
22
+ class CookbookVersionsVersionEndpoint < Endpoint
23
+ def get(request)
24
+ name, version = request.path[1], request.path[-1].gsub('_', '.')
25
+
26
+ unless cookbook = Store.find(name)
27
+ return respond(404,
28
+ {
29
+ 'error_code' => 'NOT_FOUND',
30
+ 'error_messages' => ['Resource not found'],
31
+ }
32
+ )
33
+ end
34
+
35
+ version = cookbook.latest_version if version == 'latest'
36
+ cookbook = Store.find(name, version)
37
+ respond(response_hash_for(cookbook))
38
+ end
39
+
40
+ private
41
+ # The response hash for this cookbook.
42
+ #
43
+ # @param [CommunityZero::Cookbook] cookbook
44
+ # the cookbook to generate a hash for
45
+ def response_hash_for(cookbook)
46
+ {
47
+ 'cookbook' => url_for(cookbook),
48
+ 'average_rating' => cookbook.average_rating,
49
+ 'version' => cookbook.version,
50
+ 'license' => cookbook.license,
51
+ 'file' => "http://s3.amazonaws.com/#{cookbook.name}.tgz",
52
+ 'tarball_file_size' => cookbook.name.split('').map(&:ord).inject(&:+) * 25, # don't even
53
+ 'created_at' => cookbook.created_at,
54
+ 'updated_at' => cookbook.upadated_at,
55
+ }
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,102 @@
1
+ #
2
+ # Copyright 2013, Seth Vargo <sethvargo@gmail.com>
3
+ # Copyright 2013, Opscode, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module CommunityZero
19
+ # The endpoint for all cookbooks.
20
+ #
21
+ # @author Seth Vargo <sethvargo@gmail.com>
22
+ class CookbooksEndpoint < Endpoint
23
+ require 'rubygems/package'
24
+ require 'zlib'
25
+
26
+ # GET /cookbooks
27
+ def get(request)
28
+ start = Integer(request.query_params['start'] || 0)
29
+ items = Integer(request.query_params['items'] || 10)
30
+ cookbooks = Store.cookbooks[start...items] || []
31
+
32
+ respond({
33
+ 'items' => cookbooks.collect { |cookbook|
34
+ {
35
+ 'cookbook_name' => cookbook.name,
36
+ 'cookbook_description' => cookbook.description,
37
+ 'cookbook' => url_for(cookbook),
38
+ 'cookbook_maintainer' => cookbook.maintainer
39
+ }
40
+ },
41
+ 'total' => Store.size,
42
+ 'start' => start.to_i,
43
+ })
44
+ end
45
+
46
+ # POST /cookbooks
47
+ def post(request)
48
+ params = Rack::Utils::Multipart.parse_multipart(request.env)
49
+ cookbook = params['cookbook']
50
+ tarball = params['tarball']
51
+
52
+ metadata = Metadata.new(read_tarball(tarball))
53
+
54
+ if Store.find(metadata.name, metadata.version)
55
+ respond(401,
56
+ {
57
+ 'error_code' => 'ALREADY_EXISTS',
58
+ 'error_messages' => ['Resource already exists'],
59
+ }
60
+ )
61
+ else
62
+ respond(create_cookbook(metadata).to_hash)
63
+ end
64
+ end
65
+
66
+ private
67
+ # Create the cookbook from the metadata.
68
+ #
69
+ # @param [CommunityZero::Metadata] metadata
70
+ # the metadata to create the cookbook from
71
+ def create_cookbook(metadata)
72
+ Cookbook.create(
73
+ :name => metadata.name,
74
+ :category => nil,
75
+ :maintainer => metadata.maintainer,
76
+ :description => metadata.description,
77
+ :version => metadata.version
78
+ )
79
+ end
80
+
81
+ # Parse the metadata from the tarball.
82
+ #
83
+ # @param [Tempfile] tarball
84
+ # the temporarily uploaded file
85
+ def read_tarball(tarball)
86
+ gzip = Zlib::GzipReader.new(tarball[:tempfile])
87
+ tar = Gem::Package::TarReader.new(gzip)
88
+ metadata = nil
89
+
90
+ tar.each do |entry|
91
+ if entry.full_name =~ /metadata\.rb$/
92
+ metadata = entry.read
93
+ break
94
+ end
95
+ end
96
+ tar.close
97
+
98
+ return metadata
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,37 @@
1
+ #
2
+ # Copyright 2013, Seth Vargo <sethvargo@gmail.com>
3
+ # Copyright 2013, Opscode, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module CommunityZero
19
+ # The general 404 endpoint.
20
+ #
21
+ # @author Seth Vargo <sethvargo@gmail.com>
22
+ class NotFoundEndpoint < Endpoint
23
+ def call(request)
24
+ error("Object not found: #{request.env['REQUEST_PATH']}")
25
+ end
26
+
27
+ private
28
+ def error(message)
29
+ [
30
+ 404,
31
+ { 'Content-Type' => 'application/json' },
32
+ JSON.pretty_generate({ 'error' => message })
33
+ ]
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ #
2
+ # Copyright 2013, Seth Vargo <sethvargo@gmail.com>
3
+ # Copyright 2013, Opscode, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module CommunityZero
19
+ # The endpoint for searching for cookbooks.
20
+ #
21
+ # @author Seth Vargo <sethvargo@gmail.com>
22
+ class SearchEndpoint < Endpoint
23
+ # GET /search?q=QUERY
24
+ def get(request)
25
+ q = request.query_params['q'].to_s
26
+ start = Integer(request.query_params['start'] || 0)
27
+ items = Integer(request.query_params['items'] || 10)
28
+ cookbooks = Store.search(q)[start...items] || []
29
+
30
+ respond({
31
+ 'items' => cookbooks.collect { |cookbook|
32
+ {
33
+ 'cookbook_name' => cookbook.name,
34
+ 'cookbook_description' => cookbook.description,
35
+ 'cookbook' => url_for(cookbook),
36
+ 'cookbook_maintainer' => cookbook.maintainer
37
+ }
38
+ },
39
+ 'total' => cookbooks.size,
40
+ 'start' => start,
41
+ })
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,25 @@
1
+ #
2
+ # Copyright 2013, Seth Vargo <sethvargo@gmail.com>
3
+ # Copyright 2013, Opscode, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module CommunityZero
19
+ # The base class for errors.
20
+ #
21
+ # @author Seth Vargo <sethvargo@gmail.com>
22
+ class Error < StandardError; end
23
+
24
+ require 'community_zero/errors/rest_error'
25
+ end