From 95add78f8070c0f451154b89f57bdac725da7b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mich=C3=A9e=20lengronne?= Date: Mon, 23 Nov 2020 16:02:55 +0100 Subject: [PATCH 1/6] api/v1/versions endpoint Create the api/v1/versions endpoint. --- lib/gemstash/web.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gemstash/web.rb b/lib/gemstash/web.rb index 60ec7ca5..51f0560f 100644 --- a/lib/gemstash/web.rb +++ b/lib/gemstash/web.rb @@ -45,6 +45,10 @@ def http_client_for(server_url) @gem_source.serve_dependencies_json end + get "/api/v1/versions" do + @gem_source.serve_dependencies_json + end + post "/api/v1/gems" do @gem_source.serve_add_gem end From 22b4739b7e40fbb0ee411d0070ca6e1731c9631b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mich=C3=A9e=20Lengronne?= Date: Mon, 23 Nov 2020 16:50:02 +0100 Subject: [PATCH 2/6] minimal versions endpoint --- lib/gemstash/gem_source/private_source.rb | 34 ++++++++++++++++++++++ lib/gemstash/gem_source/upstream_source.rb | 4 +++ lib/gemstash/web.rb | 4 +-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/lib/gemstash/gem_source/private_source.rb b/lib/gemstash/gem_source/private_source.rb index c35f21bc..3a21f358 100644 --- a/lib/gemstash/gem_source/private_source.rb +++ b/lib/gemstash/gem_source/private_source.rb @@ -50,6 +50,17 @@ def serve_versions halt 403, "Not yet supported" end + def serve_gem_versions(name) + authorization.protect(self) do + auth.check("fetch") if gemstash_env.config[:protected_fetch] + name.slice! ".json" + gem = fetch_gem(name) + halt 404 unless gem.exist?(:spec) + content_type "application/json;charset=UTF-8" + fetch_gem_versions(name).to_json + end + end + def serve_info(name) halt 403, "Not yet supported" end @@ -118,6 +129,29 @@ def fetch_gem(gem_full_name) halt 403, "That gem has been yanked" unless gem.properties[:indexed] gem end + + def fetch_gem_versions(gem) + results = db[" + SELECT rubygem.name, + version.number, version.platform + FROM rubygems rubygem + JOIN versions version + ON version.rubygem_id = rubygem.id + WHERE rubygem.name = ? + AND version.indexed = ?", gem.to_a, true].to_a + results.group_by {|r| r[:name] }.each do |gem, rows| + requirements = rows.group_by {|r| [r[:number], r[:platform]] } + + value = requirements.map do |version, r| + + { + :number => version.first, + :platform => version.last + } + end + + yield(gem, value) + end end end end diff --git a/lib/gemstash/gem_source/upstream_source.rb b/lib/gemstash/gem_source/upstream_source.rb index ce264bec..8fab95d7 100644 --- a/lib/gemstash/gem_source/upstream_source.rb +++ b/lib/gemstash/gem_source/upstream_source.rb @@ -62,6 +62,10 @@ def serve_versions redirect index_upstream.url("versions", request.query_string) end + def serve_gem_versions(name) + redirect index_upstream.url("api/v1/versions/#{name}", request.query_string) + end + def serve_info(name) redirect index_upstream.url("info/#{name}", request.query_string) end diff --git a/lib/gemstash/web.rb b/lib/gemstash/web.rb index 51f0560f..9bbe1258 100644 --- a/lib/gemstash/web.rb +++ b/lib/gemstash/web.rb @@ -45,8 +45,8 @@ def http_client_for(server_url) @gem_source.serve_dependencies_json end - get "/api/v1/versions" do - @gem_source.serve_dependencies_json + get "/api/v1/versions/:name" do + @gem_source.serve_gem_versions(params[:name]) end post "/api/v1/gems" do From ed1356dd683a1ac8a540e1bfb4972aa024568948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mich=C3=A9e=20Lengronne?= Date: Mon, 23 Nov 2020 17:13:56 +0100 Subject: [PATCH 3/6] tests added --- spec/gemstash/web_spec.rb | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/spec/gemstash/web_spec.rb b/spec/gemstash/web_spec.rb index 2ecc954e..eb1ae014 100644 --- a/spec/gemstash/web_spec.rb +++ b/spec/gemstash/web_spec.rb @@ -203,6 +203,62 @@ def for(server_url, timeout = 20) end end + context "GET /api/v1/versions/:name" do + context "from the default upstream" do + let(:current_env) { Gemstash::Env.current } + let(:upstream) { Gemstash::Upstream.new(current_env.config[:rubygems_url]) } + let(:storage) { Gemstash::LocalStorage.for("gem_cache").for(upstream.host_id) } + + it "fetches the gem informations as json" do + result = [{ + "number" => "1.0.0", + "platform" => "ruby" + }] + + get "/api/v1/versions/rack.json", {}, rack_env + expect(last_response).to be_ok + expect(JSON.parse(last_response.body)).to eq(result) + end + + it "can be called multiple times without error" do + get "/api/v1/versions/rack.json", {}, rack_env + get "/api/v1/versions/rack.json", {}, rack_env + end + + context "from private gems" do + let(:gem_source) { Gemstash::GemSource::PrivateSource } + let(:storage) { Gemstash::LocalStorage.for("private").for("gems") } + + context "with a missing gem" do + it "halts with 404" do + get "/api/v1/versions/unknown.json", {}, rack_env + expect(last_response).to_not be_ok + expect(last_response.status).to eq(404) + expect(last_response.body).to match(/not found/i) + end + end + + context "with a regular gem" do + before do + gem_id = insert_rubygem "example" + insert_version gem_id, "0.1.0" + storage.resource("example-0.1.0").save({ gem: "Example gem content" }, indexed: true) + end + + it "fetches the gem informations as json" do + result = [{ + "number" => "0.1.0", + "platform" => "ruby" + }] + + get "/api/v1/versions/example.json", {}, rack_env + expect(last_response).to be_ok + expect(JSON.parse(last_response.body)).to eq(result) + end + end + end + end + context "GET /gems/:id" do context "from the default upstream" do let(:current_env) { Gemstash::Env.current } From eb619cd6520c90b38a95ccb2960ad9e8d6de6d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mich=C3=A9e=20Lengronne?= Date: Tue, 24 Nov 2020 10:23:19 +0100 Subject: [PATCH 4/6] more explicit parameters --- lib/gemstash/gem_source/private_source.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gemstash/gem_source/private_source.rb b/lib/gemstash/gem_source/private_source.rb index 3a21f358..64f7f3a9 100644 --- a/lib/gemstash/gem_source/private_source.rb +++ b/lib/gemstash/gem_source/private_source.rb @@ -142,11 +142,11 @@ def fetch_gem_versions(gem) results.group_by {|r| r[:name] }.each do |gem, rows| requirements = rows.group_by {|r| [r[:number], r[:platform]] } - value = requirements.map do |version, r| + value = requirements.map do |(version, platform), r| { - :number => version.first, - :platform => version.last + :number => version, + :platform => platform } end From a853fe929ea640bb33f26dd528c78e3d1c4bcf9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mich=C3=A9e=20Lengronne?= Date: Tue, 24 Nov 2020 10:29:27 +0100 Subject: [PATCH 5/6] missing end --- lib/gemstash/gem_source/private_source.rb | 1 + lib/gemstash/web.rb | 2 +- spec/gemstash/web_spec.rb | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gemstash/gem_source/private_source.rb b/lib/gemstash/gem_source/private_source.rb index 64f7f3a9..6fa4562a 100644 --- a/lib/gemstash/gem_source/private_source.rb +++ b/lib/gemstash/gem_source/private_source.rb @@ -151,6 +151,7 @@ def fetch_gem_versions(gem) end yield(gem, value) + end end end end diff --git a/lib/gemstash/web.rb b/lib/gemstash/web.rb index 9bbe1258..6973bec5 100644 --- a/lib/gemstash/web.rb +++ b/lib/gemstash/web.rb @@ -48,7 +48,7 @@ def http_client_for(server_url) get "/api/v1/versions/:name" do @gem_source.serve_gem_versions(params[:name]) end - + post "/api/v1/gems" do @gem_source.serve_add_gem end diff --git a/spec/gemstash/web_spec.rb b/spec/gemstash/web_spec.rb index eb1ae014..92f3cc05 100644 --- a/spec/gemstash/web_spec.rb +++ b/spec/gemstash/web_spec.rb @@ -224,6 +224,7 @@ def for(server_url, timeout = 20) get "/api/v1/versions/rack.json", {}, rack_env get "/api/v1/versions/rack.json", {}, rack_env end + end context "from private gems" do let(:gem_source) { Gemstash::GemSource::PrivateSource } From 0a2c49e3d18aa4c17ee376e5bda7bbe51a280bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mich=C3=A9e=20Lengronne?= Date: Tue, 24 Nov 2020 11:27:57 +0100 Subject: [PATCH 6/6] useless variables --- lib/gemstash/gem_source/private_source.rb | 5 ++--- spec/gemstash/web_spec.rb | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/gemstash/gem_source/private_source.rb b/lib/gemstash/gem_source/private_source.rb index 6fa4562a..d616c275 100644 --- a/lib/gemstash/gem_source/private_source.rb +++ b/lib/gemstash/gem_source/private_source.rb @@ -139,11 +139,10 @@ def fetch_gem_versions(gem) ON version.rubygem_id = rubygem.id WHERE rubygem.name = ? AND version.indexed = ?", gem.to_a, true].to_a - results.group_by {|r| r[:name] }.each do |gem, rows| + results.group_by {|r| r[:name] }.each do |rows| requirements = rows.group_by {|r| [r[:number], r[:platform]] } - value = requirements.map do |(version, platform), r| - + value = requirements.map do |(version, platform)| { :number => version, :platform => platform diff --git a/spec/gemstash/web_spec.rb b/spec/gemstash/web_spec.rb index 92f3cc05..881e0242 100644 --- a/spec/gemstash/web_spec.rb +++ b/spec/gemstash/web_spec.rb @@ -229,7 +229,7 @@ def for(server_url, timeout = 20) context "from private gems" do let(:gem_source) { Gemstash::GemSource::PrivateSource } let(:storage) { Gemstash::LocalStorage.for("private").for("gems") } - + context "with a missing gem" do it "halts with 404" do get "/api/v1/versions/unknown.json", {}, rack_env @@ -238,14 +238,14 @@ def for(server_url, timeout = 20) expect(last_response.body).to match(/not found/i) end end - + context "with a regular gem" do before do gem_id = insert_rubygem "example" insert_version gem_id, "0.1.0" storage.resource("example-0.1.0").save({ gem: "Example gem content" }, indexed: true) end - + it "fetches the gem informations as json" do result = [{ "number" => "0.1.0", @@ -257,7 +257,7 @@ def for(server_url, timeout = 20) expect(JSON.parse(last_response.body)).to eq(result) end end - end + end end context "GET /gems/:id" do