Skip to content

Commit 22ca313

Browse files
committed
(#806) Add API for uploading JSON contexts
1 parent 5028e72 commit 22ca313

File tree

8 files changed

+297
-0
lines changed

8 files changed

+297
-0
lines changed

app/api/entities/json_context.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module API
2+
module Entities
3+
# Presenter for JsonContext
4+
class JsonContext < Grape::Entity
5+
expose :context,
6+
documentation: { type: 'object',
7+
desc: 'The payload of the context' }
8+
expose :url,
9+
documentation: { type: 'string',
10+
desc: 'The URL of the context' }
11+
end
12+
end
13+
end

app/api/v1/base.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
require 'v1/config'
1515
require 'v1/bulk_purge'
1616
require 'v1/ctdl'
17+
require 'v1/json_contexts'
1718

1819
module API
1920
module V1
@@ -59,6 +60,7 @@ class Base < Grape::API
5960
end
6061

6162
mount API::V1::Config
63+
mount API::V1::JsonContexts
6264
mount API::V1::Organizations
6365
mount API::V1::Publishers
6466
end

app/api/v1/json_contexts.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
require 'entities/json_context'
2+
require 'policies/json_context_policy'
3+
4+
module API
5+
module V1
6+
# Json contexts API endpoints
7+
class JsonContexts < Grape::API
8+
helpers SharedHelpers
9+
10+
before do
11+
authenticate!
12+
end
13+
14+
resources :json_contexts do # rubocop:todo Metrics/BlockLength
15+
desc 'Retrieves all JSON contexts'
16+
get do
17+
authorize JsonContext, :index?
18+
present JsonContext.order(updated_at: :desc), with: Entities::JsonContext
19+
end
20+
21+
desc 'Retrives a JSON context by its URL'
22+
params do
23+
requires :url, type: String, desc: 'Context URL'
24+
end
25+
get ':url', requirements: { url: /(.*)/i } do
26+
authorize JsonContext, :index?
27+
json_context = JsonContext.find_by!(url: params[:url])
28+
present json_context, with: Entities::JsonContext
29+
end
30+
31+
desc 'Uploads a JSON context'
32+
params do
33+
requires :context, type: Hash, desc: 'Context payload'
34+
requires :url, type: String, desc: 'Context URL'
35+
end
36+
post do
37+
authorize JsonContext, :create?
38+
json_context = JsonContext.find_or_initialize_by(url: params[:url])
39+
status json_context.new_record? ? :created : :ok
40+
json_context.update!(context: params[:context])
41+
present json_context, with: Entities::JsonContext
42+
end
43+
end
44+
end
45+
end
46+
end

app/policies/json_context_policy.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
require_relative 'application_policy'
2+
3+
# Specifies policies for organization API
4+
class JsonContextPolicy < ApplicationPolicy
5+
def index?
6+
user.admin?
7+
end
8+
9+
def create?
10+
user.admin?
11+
end
12+
end

lib/swagger_docs/models.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,16 @@ module Models # rubocop:todo Metrics/ModuleLength, Style/Documentation
503503
type: :string,
504504
format: :datetime
505505
end
506+
507+
swagger_schema :JsonContext do
508+
property :context,
509+
description: 'The payload of the context',
510+
type: :object
511+
512+
property :url,
513+
description: 'The URL of the context',
514+
type: :string
515+
end
506516
end
507517
end
508518
end

lib/swagger_docs/sections/admin.rb

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,81 @@ module Admin # rubocop:todo Metrics/ModuleLength, Style/Documentation
77
extend ActiveSupport::Concern
88

99
included do
10+
swagger_path '/metadata/json_contexts' do
11+
operation :get do
12+
key :operationId, 'getApiJsonContexts'
13+
key :description, 'Retrieves all JSON contexts'
14+
key :produces, ['application/json']
15+
key :tags, ['Admin']
16+
17+
response 200 do
18+
key :description, 'JSON context updated'
19+
schema do
20+
key :type, :array
21+
items { key :$ref, :JsonContext }
22+
end
23+
end
24+
end
25+
26+
operation :post do # rubocop:todo Metrics/BlockLength
27+
key :operationId, 'postApiJsonContexts'
28+
key :description, 'Uploads a JSON context'
29+
key :produces, ['application/json']
30+
key :tags, ['Admin']
31+
32+
parameter do
33+
key :name, :body
34+
key :in, :body
35+
key :description, 'Request body'
36+
key :required, true
37+
38+
schema do
39+
key :required, %i[context url]
40+
41+
property :context do
42+
key :type, :object
43+
key :description, 'Context payload'
44+
end
45+
46+
property :url do
47+
key :type, :string
48+
key :description, 'Context URL'
49+
end
50+
end
51+
end
52+
53+
response 200 do
54+
key :description, 'JSON context updated'
55+
schema { key :$ref, :JsonContext }
56+
end
57+
58+
response 201 do
59+
key :description, 'JSON context created'
60+
schema { key :$ref, :JsonContext }
61+
end
62+
end
63+
end
64+
65+
swagger_path '/metadata/json_contexts/{url}' do
66+
operation :get do
67+
key :operationId, 'getApiJsonContext'
68+
key :description, 'Retrieves a JSON context by its URL'
69+
key :produces, ['application/json']
70+
key :tags, ['Admin']
71+
72+
parameter name: :url,
73+
in: :path,
74+
type: :string,
75+
required: true,
76+
description: 'The URL of the JSON context'
77+
78+
response 200 do
79+
key :description, 'JSON context updated'
80+
schema { key :$ref, :JsonContext }
81+
end
82+
end
83+
end
84+
1085
swagger_path '/metadata/organizations' do
1186
operation :get do
1287
key :operationId, 'getApiOrganizations'

spec/api/v1/json_contexts_spec.rb

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
require_relative 'shared_examples/auth'
2+
3+
RSpec.describe 'Json contexts API' do # rubocop:todo RSpec/DescribeClass
4+
let(:admin) { token.admin }
5+
let(:admin_token) { create(:auth_token, :admin) }
6+
let(:url) { Faker::Internet.url }
7+
8+
describe 'GET /metadata/json_contexts' do
9+
let!(:json_context1) { create(:json_context) } # rubocop:todo RSpec/IndexedLet
10+
let!(:json_context2) { create(:json_context) } # rubocop:todo RSpec/IndexedLet
11+
12+
include_examples 'requires auth', :post, '/metadata/json_contexts'
13+
14+
it 'returns all JSON contexts' do
15+
get '/metadata/json_contexts', 'Authorization' => "Token #{admin_token.value}"
16+
expect_status(:ok)
17+
expect_json('0.context', **json_context2.context.symbolize_keys)
18+
expect_json('0.url', json_context2.url)
19+
expect_json('1.context', **json_context1.context.symbolize_keys)
20+
expect_json('1.url', json_context1.url)
21+
end
22+
end
23+
24+
describe 'GET /metadata/json_contexts/:url' do
25+
let!(:json_context) { create(:json_context) }
26+
27+
include_examples 'requires auth', :post, '/metadata/json_contexts/0'
28+
29+
it 'returns the JSON context with the given URL' do
30+
get "/metadata/json_contexts/#{CGI.escape(json_context.url)}",
31+
'Authorization' => "Token #{admin_token.value}"
32+
expect_status(:ok)
33+
expect_json('context', **json_context.context.symbolize_keys)
34+
expect_json('url', json_context.url)
35+
end
36+
end
37+
38+
describe 'POST /metadata/json_contexts' do
39+
let(:context) { JSON(Faker::Json.shallow_json) }
40+
let(:params) { { context:, url: } }
41+
42+
include_examples 'requires auth', :post, '/metadata/json_contexts'
43+
44+
context 'as admin' do # rubocop:todo RSpec/ContextWording
45+
before do
46+
post '/metadata/json_contexts',
47+
params,
48+
'Authorization' => "Token #{admin_token.value}"
49+
end
50+
51+
# rubocop:todo RSpec/NestedGroups
52+
context 'new context' do # rubocop:todo RSpec/ContextWording, RSpec/NestedGroups
53+
# rubocop:enable RSpec/NestedGroups
54+
# rubocop:todo RSpec/NestedGroups
55+
context 'empty context' do # rubocop:todo RSpec/ContextWording, RSpec/NestedGroups
56+
# rubocop:enable RSpec/NestedGroups
57+
let(:context) { nil }
58+
59+
it 'returns 422' do
60+
expect_status(:unprocessable_entity)
61+
expect_json('error', "Context can't be blank")
62+
end
63+
end
64+
65+
# rubocop:todo RSpec/NestedGroups
66+
context 'empty URL' do # rubocop:todo RSpec/ContextWording, RSpec/NestedGroups
67+
let(:url) { nil }
68+
69+
# rubocop:enable RSpec/NestedGroups
70+
it 'return 422' do
71+
expect_status(:unprocessable_entity)
72+
expect_json('error', "Url can't be blank")
73+
end
74+
end
75+
76+
context 'all params' do # rubocop:todo RSpec/ContextWording, RSpec/NestedGroups
77+
it 'creates a next context' do
78+
json_context = JsonContext.order(:created_at).last
79+
expect(json_context.context).to eq(context)
80+
expect(json_context.url).to eq(url)
81+
expect_status(:created)
82+
expect_json('context', **context.symbolize_keys)
83+
expect_json('url', url)
84+
end
85+
end
86+
end
87+
88+
# rubocop:todo RSpec/MultipleMemoizedHelpers
89+
# rubocop:todo RSpec/NestedGroups
90+
context 'existing context' do # rubocop:todo RSpec/ContextWording, RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups
91+
let(:json_context) { create(:json_context) }
92+
let(:url) { json_context.url }
93+
94+
# rubocop:todo RSpec/MultipleMemoizedHelpers
95+
# rubocop:todo RSpec/NestedGroups
96+
context 'empty context' do # rubocop:todo RSpec/ContextWording, RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups
97+
let(:context) { nil }
98+
99+
# rubocop:enable RSpec/NestedGroups
100+
it 'returns 422' do
101+
expect_status(:unprocessable_entity)
102+
expect_json('error', "Context can't be blank")
103+
end
104+
end
105+
# rubocop:enable RSpec/MultipleMemoizedHelpers
106+
107+
context 'all params' do # rubocop:todo RSpec/ContextWording, RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups
108+
it 'updates the context' do
109+
json_context.reload
110+
expect(json_context.context).to eq(context)
111+
expect(json_context.url).to eq(url)
112+
expect_status(:ok)
113+
expect_json('context', **context.symbolize_keys)
114+
expect_json('url', url)
115+
end
116+
end
117+
end
118+
# rubocop:enable RSpec/MultipleMemoizedHelpers
119+
end
120+
121+
# rubocop:todo RSpec/MultipleMemoizedHelpers
122+
context 'as publisher' do # rubocop:todo RSpec/ContextWording
123+
let(:token) { create(:auth_token) }
124+
125+
it 'returns 403' do
126+
post '/metadata/json_contexts',
127+
params,
128+
'Authorization' => "Token #{token.value}"
129+
expect_status(:forbidden)
130+
end
131+
end
132+
# rubocop:enable RSpec/MultipleMemoizedHelpers
133+
end
134+
end

spec/factories/json_contexts.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FactoryBot.define do
2+
factory :json_context do
3+
url { Faker::Internet.url }
4+
end
5+
end

0 commit comments

Comments
 (0)