Skip to content

Commit

Permalink
Add /v3/releases/:module endpoint. Bug fixes. Add some unit tests.
Browse files Browse the repository at this point in the history
1. Fix json content type
2. Add v3 releases end-point
3. Fix bug in logger constructor
4. Fix Gem to puppet version conversion
5. Replace / to - in module names for v3 proxy backend
6. Fix loggoing in v3 modules api
7. Add a few UTs
8. Add Rakefile
9. Update travis to run test:all task
10. Bump version
  • Loading branch information
Ilja Bobkevic committed Dec 6, 2014
1 parent 8af819a commit a02e063
Show file tree
Hide file tree
Showing 17 changed files with 305 additions and 44 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@
language: ruby
rvm:
- 1.9.3
- 2.0.0
- 2.0.0

script:
- bundle exec rake test:all
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ Puppet module *metadata* json representation is used as a main business *model*.
1. Create UTs for core logic
2. Implement *source* and *git* backends to match [puppet library](https://github.com/drrb/puppet-library) feature set

## Limitations

1. Modulefile is not supported with the *directory* backend

## License

[Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
Expand Down
32 changes: 32 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- encoding: utf-8 -*-
#
# Copyright 2014 North Development AB
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new('test:integration') do |t|
t.verbose = true
t.pattern = 'spec/integration/*_spec.rb'
end

RSpec::Core::RakeTask.new('test:unit') do |t|
t.verbose = true
t.pattern = 'spec/unit/*_spec.rb'
end

RSpec::Core::RakeTask.new('test:all') do |t|
t.verbose = true
t.pattern = 'spec/**/*_spec.rb'
end
2 changes: 1 addition & 1 deletion lib/puppet_forge_server/api/v3/modules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def get_modules(metadata)
end
end

PuppetForge::Logger.get.error 'WARNING: Requested module count is more than 1' unless modules.values.count == 1
PuppetForgeServer::Logger.get.error "Requested module count is more than 1:\n#{modules.values}" unless modules.values.count > 1
modules.values.first
end

Expand Down
8 changes: 5 additions & 3 deletions lib/puppet_forge_server/app/generic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# limitations under the License.

require 'sinatra/base'
require 'sinatra/json'

module PuppetForgeServer::App
class Generic < Sinatra::Base
Expand All @@ -24,9 +25,10 @@ class Generic < Sinatra::Base
use ::Rack::CommonLogger, PuppetForgeServer::Logger.get(:access)
end

before {
before do
content_type :json
env['rack.errors'] = PuppetForgeServer::Logger.get(:server)
}
end

def initialize
super(nil)
Expand All @@ -37,7 +39,7 @@ def initialize
#
post '/oauth/token' do
PuppetForgeServer::Logger.get(:server).debug "Params: #{params}"
{'access_token' => 'BOGUS_ACCESS_TOKEN'}.to_json
json :access_token => 'BOGUS_ACCESS_TOKEN'
end

end
Expand Down
21 changes: 10 additions & 11 deletions lib/puppet_forge_server/app/version1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# limitations under the License.

require 'sinatra/base'
require 'json'
require 'sinatra/json'

module PuppetForgeServer::App
class Version1 < Sinatra::Base
Expand All @@ -28,19 +28,18 @@ class Version1 < Sinatra::Base
use ::Rack::CommonLogger, PuppetForgeServer::Logger.get(:access)
end

before {
env['rack.errors'] = PuppetForgeServer::Logger.get(:server)
}
before do
content_type :json
env['rack.errors'] = PuppetForgeServer::Logger.get(:server)
end

def initialize(backends)
super(nil)
@backends = backends
end

get '/api/v1/releases.json' do
unless params[:module]
halt 400, {'error' => 'The number of version constraints in the query does not match the number of module names'}.to_json
end
halt 400, json({:error => 'The number of version constraints in the query does not match the number of module names'}) unless params[:module]

author, name = params[:module].split '/'
version = params[:version] if params[:version]
Expand All @@ -49,16 +48,16 @@ def initialize(backends)
backend.get_metadata(author, name, {:version => version, :with_checksum => false})
end.flatten.compact.uniq

halt 400, {'errors' => ["'#{params[:module]}' is not a valid module slug"]}.to_json if metadata.empty?
halt 400, json({:errors => ["'#{params[:module]}' is not a valid module slug"]}) if metadata.empty?

{"#{author}/#{name}" => get_releases(metadata)}.to_json
json "#{author}/#{name}" => get_releases(metadata)
end

get '/api/v1/files/*' do
captures = params[:captures].first
buffer = get_buffer(@backends, captures)

halt 404, {'errors' => ['404 Not found']}.to_json unless buffer
halt 404, json({:errors => ['404 Not found']}) unless buffer

content_type 'application/octet-stream'
attachment captures.split('/').last
Expand All @@ -70,7 +69,7 @@ def initialize(backends)
metadata = @backends.map do |backend|
backend.query_metadata(query, {:with_checksum => false})
end.flatten.compact.uniq
get_modules(metadata).to_json
json get_modules(metadata)
end
end
end
9 changes: 5 additions & 4 deletions lib/puppet_forge_server/app/version2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# limitations under the License.

require 'sinatra/base'
require 'sinatra/json'

module PuppetForgeServer::App
class Version2 < Sinatra::Base
Expand All @@ -24,17 +25,17 @@ class Version2 < Sinatra::Base
use ::Rack::CommonLogger, PuppetForgeServer::Logger.get(:access)
end

before {
env['rack.errors'] = PuppetForgeServer::Logger.get(:server)
}
before do
env['rack.errors'] = PuppetForgeServer::Logger.get(:server)
end

def initialize(backends)
super(nil)
@backends = backends
end

post '/v2/releases' do
halt 410, {'errors' => ['File not provided']}.to_json unless params['file']
halt 410, json({:errors => ['File not provided']}) unless params['file']
states = @backends.map do |backend|
backend.upload(params['file'])
end.compact
Expand Down
47 changes: 30 additions & 17 deletions lib/puppet_forge_server/app/version3.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# limitations under the License.

require 'sinatra/base'
require 'sinatra/json'

module PuppetForgeServer::App
class Version3 < Sinatra::Base
Expand All @@ -27,34 +28,38 @@ class Version3 < Sinatra::Base
use ::Rack::CommonLogger, PuppetForgeServer::Logger.get(:access)
end

before {
env['rack.errors'] = PuppetForgeServer::Logger.get(:server)
}
before do
content_type :json
env['rack.errors'] = PuppetForgeServer::Logger.get(:server)
end

def initialize(backends)
super(nil)
@backends = backends
end

get '/v3/releases' do
halt 400, {'error' => 'The number of version constraints in the query does not match the number of module names'}.to_json unless params[:module]

get '/v3/releases/:module' do
halt 404, json({:errors => ['404 Not found']}) unless params[:module]
author, name, version = params[:module].split '-'
metadata = @backends.map do |backend|
backend.get_metadata(author, name, {:version => version})
end.flatten.compact.uniq

halt 404, {'pagination' => {'next' => false}, 'results' => []}.to_json if metadata.empty?
halt 400, json({:errors => ["'#{params[:module]}' is not a valid release slug"]}) unless author && name && version
releases = releases(author, name, version)
halt 404, json({:errors => ['404 Not found']}) unless releases
PuppetForgeServer::Logger.get(:server).error "Requested releases count is more than 1:\n#{releases}" unless releases.count > 1
json releases.first
end

releases = get_releases(metadata)
{'pagination' => {'next' => false, 'total' => releases.count}, 'results' => releases}.to_json
get '/v3/releases' do
halt 400, json({:error => 'The number of version constraints in the query does not match the number of module names'}) unless params[:module]
releases = releases(params[:module].split '-')
halt 404, json({:pagination => {:next => false}, :results => []}) unless releases
json :pagination => {:next => false, :total => releases.count}, :results => releases
end

get '/v3/files/*' do
captures = params[:captures].first
buffer = get_buffer(@backends, captures)

halt 404, {'errors' => ['404 Not found']}.to_json unless buffer
halt 404, json({:errors => ['404 Not found']}) unless buffer

content_type 'application/octet-stream'
attachment captures.split('/').last
Expand All @@ -65,15 +70,23 @@ def initialize(backends)
author = params[:author]
name = params[:name]

halt 400, {'errors' => "'#{params[:module]}' is not a valid module slug"}.to_json unless author && name
halt 400, json({:errors => "'#{params[:module]}' is not a valid module slug"}) unless author && name

metadata = @backends.map do |backend|
backend.get_metadata(author, name)
end.flatten.compact.uniq

halt 404, {'errors' => ['404 Not found']}.to_json if metadata.empty?
halt 404, json({:errors => ['404 Not found']}) if metadata.empty?

json get_modules(metadata)
end

get_modules(metadata).to_json
private
def releases(author, name, version = nil)
metadata = @backends.map do |backend|
backend.get_metadata(author, name, {:version => version})
end.flatten.compact.uniq
metadata.empty? ? nil : get_releases(metadata)
end
end
end
7 changes: 6 additions & 1 deletion lib/puppet_forge_server/backends/proxy_v3.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,15 @@ def get_all_release_pages(next_page)
releases
end

def normalize_metadata(metadata)
metadata['name'] = metadata['name'].gsub('/', '-')
metadata
end

def get_release_metadata(releases)
releases.map do |element|
{
:metadata => PuppetForgeServer::Models::Metadata.new(element['metadata']),
:metadata => PuppetForgeServer::Models::Metadata.new(normalize_metadata(element['metadata'])),
:checksum => element['file_md5'],
:path => element['file_uri'],
:tags => (element['tags'] + (element['metadata']['tags'] ? element['metadata']['tags'] : [])).flatten.uniq
Expand Down
6 changes: 3 additions & 3 deletions lib/puppet_forge_server/logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class Logger
@@DEFAULT_DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
@static_loggers = {:server => nil, :access => nil}

def initialize(destinations = [@@DEFAULT_LOGGER])
@loggers = Array.new(destinations).flatten.map do |dest|
def initialize(destinations = [@@DEFAULT_DESTINATION])
@loggers = [destinations].flatten.map do |dest|
logger = ::Logger.new(dest)
logger.formatter = proc do |severity, datetime, progname, msg|
datetime = datetime.strftime @@DEFAULT_DATETIME_FORMAT
Expand All @@ -45,7 +45,7 @@ def method_missing (method_name, *args, &block)
end

def respond_to?(method_name, include_private = false)
@loggers.each { |logger| false unless logger.respond_to? method_name }
@loggers.each { |logger| return false unless (logger.respond_to?(method_name) || %w(write puts).include?(method_name.to_s)) }
end

class << self
Expand Down
15 changes: 13 additions & 2 deletions lib/puppet_forge_server/patches.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@

require 'rubygems/package'

module Gem
def Version.new(version)
super(version.to_s.gsub('-', '.pre.'))
rescue ArgumentError
if version =~ /^\d+(\.\d+)*/
super(version[/^\d+(\.\d+)*/])
else
super('0')
end
end
end

class Hash
def deep_merge(other)
merge(other) do |key, old_val, new_val|
Expand All @@ -42,8 +54,7 @@ def deep_merge

def version_sort_by
sort_by do |element|
version = yield(element).gsub('-', '.pre.')
Gem::Version.new(version)
Gem::Version.new(yield(element))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/puppet_forge_server/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
# limitations under the License.

module PuppetForgeServer
VERSION = '1.1.0'
VERSION = '1.2.0'
end
2 changes: 2 additions & 0 deletions puppet-forge-server.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ Gem::Specification.new do |spec|
spec.require_paths = %w(lib)

spec.add_dependency 'sinatra', '~> 1.4'
spec.add_dependency 'sinatra-contrib', '~> 1.4'
spec.add_dependency 'json', '~> 1.8'
spec.add_dependency 'rack-mount', '~> 0.8'
spec.add_dependency 'open4', '~> 1.3'
spec.add_dependency 'open_uri_redirections', '~> 0.1'

spec.add_development_dependency 'rake', '~> 10.3'
spec.add_development_dependency 'rspec', '~> 3.1'
spec.add_development_dependency 'rspec-core', '~> 3.1'

spec.required_ruby_version = '>= 1.9.3'
end
30 changes: 30 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- encoding: utf-8 -*-
#
# Copyright 2014 North Development AB
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'rack/test'
require 'rspec'
require 'puppet_forge_server'

ENV['RACK_ENV'] = 'test'

module RSpecMixin
include Rack::Test::Methods
end

RSpec.configure do |c|
c.include RSpecMixin
c.add_formatter(:documentation)
end
Loading

0 comments on commit a02e063

Please sign in to comment.