Convert /instances/* controller specs to request specs (#27988)
		
	Co-authored-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
		
					parent
					
						
							
								718c95e7af
							
						
					
				
			
			
				commit
				
					
						876f5b1d12
					
				
			
		
					 20 changed files with 349 additions and 192 deletions
				
			
		|  | @ -3,6 +3,8 @@ | |||
| class Api::V1::Instances::ActivityController < Api::V1::Instances::BaseController | ||||
|   before_action :require_enabled_api! | ||||
| 
 | ||||
|   WEEKS_OF_ACTIVITY = 12 | ||||
| 
 | ||||
|   def show | ||||
|     cache_even_if_authenticated! | ||||
|     render_with_cache json: :activity, expires_in: 1.day | ||||
|  | @ -11,23 +13,40 @@ class Api::V1::Instances::ActivityController < Api::V1::Instances::BaseControlle | |||
|   private | ||||
| 
 | ||||
|   def activity | ||||
|     statuses_tracker      = ActivityTracker.new('activity:statuses:local', :basic) | ||||
|     logins_tracker        = ActivityTracker.new('activity:logins', :unique) | ||||
|     registrations_tracker = ActivityTracker.new('activity:accounts:local', :basic) | ||||
| 
 | ||||
|     (0...12).map do |i| | ||||
|       start_of_week = i.weeks.ago | ||||
|       end_of_week   = start_of_week + 6.days | ||||
| 
 | ||||
|       { | ||||
|         week: start_of_week.to_i.to_s, | ||||
|         statuses: statuses_tracker.sum(start_of_week, end_of_week).to_s, | ||||
|         logins: logins_tracker.sum(start_of_week, end_of_week).to_s, | ||||
|         registrations: registrations_tracker.sum(start_of_week, end_of_week).to_s, | ||||
|       } | ||||
|     activity_weeks.map do |weeks_ago| | ||||
|       activity_json(*week_edge_days(weeks_ago)) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def activity_json(start_of_week, end_of_week) | ||||
|     { | ||||
|       week: start_of_week.to_i.to_s, | ||||
|       statuses: statuses_tracker.sum(start_of_week, end_of_week).to_s, | ||||
|       logins: logins_tracker.sum(start_of_week, end_of_week).to_s, | ||||
|       registrations: registrations_tracker.sum(start_of_week, end_of_week).to_s, | ||||
|     } | ||||
|   end | ||||
| 
 | ||||
|   def activity_weeks | ||||
|     0...WEEKS_OF_ACTIVITY | ||||
|   end | ||||
| 
 | ||||
|   def week_edge_days(num) | ||||
|     [num.weeks.ago, num.weeks.ago + 6.days] | ||||
|   end | ||||
| 
 | ||||
|   def statuses_tracker | ||||
|     ActivityTracker.new('activity:statuses:local', :basic) | ||||
|   end | ||||
| 
 | ||||
|   def logins_tracker | ||||
|     ActivityTracker.new('activity:logins', :unique) | ||||
|   end | ||||
| 
 | ||||
|   def registrations_tracker | ||||
|     ActivityTracker.new('activity:accounts:local', :basic) | ||||
|   end | ||||
| 
 | ||||
|   def require_enabled_api! | ||||
|     head 404 unless Setting.activity_api_enabled && !limited_federation_mode? | ||||
|   end | ||||
|  |  | |||
|  | @ -19,7 +19,19 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr | |||
|   private | ||||
| 
 | ||||
|   def require_enabled_api! | ||||
|     head 404 unless Setting.show_domain_blocks == 'all' || (Setting.show_domain_blocks == 'users' && user_signed_in?) | ||||
|     head 404 unless api_enabled? | ||||
|   end | ||||
| 
 | ||||
|   def api_enabled? | ||||
|     show_domain_blocks_for_all? || show_domain_blocks_to_user? | ||||
|   end | ||||
| 
 | ||||
|   def show_domain_blocks_for_all? | ||||
|     Setting.show_domain_blocks == 'all' | ||||
|   end | ||||
| 
 | ||||
|   def show_domain_blocks_to_user? | ||||
|     Setting.show_domain_blocks == 'users' && user_signed_in? | ||||
|   end | ||||
| 
 | ||||
|   def set_domain_blocks | ||||
|  |  | |||
|  | @ -1,21 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe Api::V1::Instances::ActivityController do | ||||
|   describe 'GET #show' do | ||||
|     it 'returns 200' do | ||||
|       get :show | ||||
|       expect(response).to have_http_status(200) | ||||
|     end | ||||
| 
 | ||||
|     context 'with !Setting.activity_api_enabled' do | ||||
|       it 'returns 404' do | ||||
|         Setting.activity_api_enabled = false | ||||
| 
 | ||||
|         get :show | ||||
|         expect(response).to have_http_status(404) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,16 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| describe Api::V1::Instances::DomainBlocksController do | ||||
|   render_views | ||||
| 
 | ||||
|   describe 'GET #index' do | ||||
|     it 'returns http success' do | ||||
|       Setting.show_domain_blocks = 'all' | ||||
|       get :index | ||||
| 
 | ||||
|       expect(response).to have_http_status(200) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,15 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| describe Api::V1::Instances::ExtendedDescriptionsController do | ||||
|   render_views | ||||
| 
 | ||||
|   describe 'GET #show' do | ||||
|     it 'returns http success' do | ||||
|       get :show | ||||
| 
 | ||||
|       expect(response).to have_http_status(200) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,21 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe Api::V1::Instances::PeersController do | ||||
|   describe 'GET #index' do | ||||
|     it 'returns 200' do | ||||
|       get :index | ||||
|       expect(response).to have_http_status(200) | ||||
|     end | ||||
| 
 | ||||
|     context 'with !Setting.peers_api_enabled' do | ||||
|       it 'returns 404' do | ||||
|         Setting.peers_api_enabled = false | ||||
| 
 | ||||
|         get :index | ||||
|         expect(response).to have_http_status(404) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,15 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| describe Api::V1::Instances::PrivacyPoliciesController do | ||||
|   render_views | ||||
| 
 | ||||
|   describe 'GET #show' do | ||||
|     it 'returns http success' do | ||||
|       get :show | ||||
| 
 | ||||
|       expect(response).to have_http_status(200) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,15 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| describe Api::V1::Instances::RulesController do | ||||
|   render_views | ||||
| 
 | ||||
|   describe 'GET #index' do | ||||
|     it 'returns http success' do | ||||
|       get :index | ||||
| 
 | ||||
|       expect(response).to have_http_status(200) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,30 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| describe Api::V1::Instances::TranslationLanguagesController do | ||||
|   describe 'GET #show' do | ||||
|     context 'when no translation service is configured' do | ||||
|       it 'returns empty language matrix', :aggregate_failures do | ||||
|         get :show | ||||
| 
 | ||||
|         expect(response).to have_http_status(200) | ||||
|         expect(body_as_json).to eq({}) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when a translation service is configured' do | ||||
|       before do | ||||
|         service = instance_double(TranslationService::DeepL, languages: { nil => %w(en de), 'en' => ['de'] }) | ||||
|         allow(TranslationService).to receive_messages(configured?: true, configured: service) | ||||
|       end | ||||
| 
 | ||||
|       it 'returns language matrix', :aggregate_failures do | ||||
|         get :show | ||||
| 
 | ||||
|         expect(response).to have_http_status(200) | ||||
|         expect(body_as_json).to eq({ und: %w(en de), en: ['de'] }) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,22 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe Api::V1::InstancesController do | ||||
|   render_views | ||||
| 
 | ||||
|   let(:user)  { Fabricate(:user) } | ||||
|   let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id) } | ||||
| 
 | ||||
|   before do | ||||
|     allow(controller).to receive(:doorkeeper_token) { token } | ||||
|   end | ||||
| 
 | ||||
|   describe 'GET #show' do | ||||
|     it 'returns http success' do | ||||
|       get :show | ||||
| 
 | ||||
|       expect(response).to have_http_status(200) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,22 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| describe Api::V2::InstancesController do | ||||
|   render_views | ||||
| 
 | ||||
|   let(:user)  { Fabricate(:user) } | ||||
|   let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id) } | ||||
| 
 | ||||
|   before do | ||||
|     allow(controller).to receive(:doorkeeper_token) { token } | ||||
|   end | ||||
| 
 | ||||
|   describe 'GET #show' do | ||||
|     it 'returns http success' do | ||||
|       get :show | ||||
| 
 | ||||
|       expect(response).to have_http_status(200) | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										37
									
								
								spec/requests/api/v1/instance_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								spec/requests/api/v1/instance_spec.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| describe 'Instances' do | ||||
|   let(:user)    { Fabricate(:user) } | ||||
|   let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id) } | ||||
|   let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } | ||||
| 
 | ||||
|   describe 'GET /api/v1/instance' do | ||||
|     context 'when not logged in' do | ||||
|       it 'returns http success and json' do | ||||
|         get api_v1_instance_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(200) | ||||
| 
 | ||||
|         expect(body_as_json) | ||||
|           .to be_present | ||||
|           .and include(title: 'Mastodon') | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when logged in' do | ||||
|       it 'returns http success and json' do | ||||
|         get api_v1_instance_path, headers: headers | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(200) | ||||
| 
 | ||||
|         expect(body_as_json) | ||||
|           .to be_present | ||||
|           .and include(title: 'Mastodon') | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										40
									
								
								spec/requests/api/v1/instances/activity_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								spec/requests/api/v1/instances/activity_spec.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe 'Activity' do | ||||
|   describe 'GET /api/v1/instance/activity' do | ||||
|     around do |example| | ||||
|       original = Setting.activity_api_enabled | ||||
|       example.run | ||||
|       Setting.activity_api_enabled = original | ||||
|     end | ||||
| 
 | ||||
|     context 'with activity api enabled' do | ||||
|       before { Setting.activity_api_enabled = true } | ||||
| 
 | ||||
|       it 'returns http success' do | ||||
|         get api_v1_instance_activity_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(200) | ||||
| 
 | ||||
|         expect(body_as_json) | ||||
|           .to be_present | ||||
|           .and(be_an(Array)) | ||||
|           .and(have_attributes(size: Api::V1::Instances::ActivityController::WEEKS_OF_ACTIVITY)) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'with activity api diabled' do | ||||
|       before { Setting.activity_api_enabled = false } | ||||
| 
 | ||||
|       it 'returns not found' do | ||||
|         get api_v1_instance_activity_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(404) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										55
									
								
								spec/requests/api/v1/instances/domain_blocks_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								spec/requests/api/v1/instances/domain_blocks_spec.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe 'Domain Blocks' do | ||||
|   describe 'GET /api/v1/instance/domain_blocks' do | ||||
|     around do |example| | ||||
|       original = Setting.show_domain_blocks | ||||
|       example.run | ||||
|       Setting.show_domain_blocks = original | ||||
|     end | ||||
| 
 | ||||
|     before do | ||||
|       Fabricate(:domain_block) | ||||
|     end | ||||
| 
 | ||||
|     context 'with domain blocks set to all' do | ||||
|       before { Setting.show_domain_blocks = 'all' } | ||||
| 
 | ||||
|       it 'returns http success' do | ||||
|         get api_v1_instance_domain_blocks_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(200) | ||||
| 
 | ||||
|         expect(body_as_json) | ||||
|           .to be_present | ||||
|           .and(be_an(Array)) | ||||
|           .and(have_attributes(size: 1)) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'with domain blocks set to users' do | ||||
|       before { Setting.show_domain_blocks = 'users' } | ||||
| 
 | ||||
|       it 'returns http not found' do | ||||
|         get api_v1_instance_domain_blocks_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(404) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'with domain blocks set to disabled' do | ||||
|       before { Setting.show_domain_blocks = 'disabled' } | ||||
| 
 | ||||
|       it 'returns http not found' do | ||||
|         get api_v1_instance_domain_blocks_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(404) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										18
									
								
								spec/requests/api/v1/instances/extended_descriptions_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								spec/requests/api/v1/instances/extended_descriptions_spec.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe 'Extended Descriptions' do | ||||
|   describe 'GET /api/v1/instance/extended_description' do | ||||
|     it 'returns http success' do | ||||
|       get api_v1_instance_extended_description_path | ||||
| 
 | ||||
|       expect(response) | ||||
|         .to have_http_status(200) | ||||
| 
 | ||||
|       expect(body_as_json) | ||||
|         .to be_present | ||||
|         .and include(:content) | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										38
									
								
								spec/requests/api/v1/instances/peers_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								spec/requests/api/v1/instances/peers_spec.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe 'Peers' do | ||||
|   describe 'GET /api/v1/instance/peers' do | ||||
|     around do |example| | ||||
|       original = Setting.peers_api_enabled | ||||
|       example.run | ||||
|       Setting.peers_api_enabled = original | ||||
|     end | ||||
| 
 | ||||
|     context 'with peers api enabled' do | ||||
|       before { Setting.peers_api_enabled = true } | ||||
| 
 | ||||
|       it 'returns http success' do | ||||
|         get api_v1_instance_peers_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(200) | ||||
| 
 | ||||
|         expect(body_as_json) | ||||
|           .to be_an(Array) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'with peers api diabled' do | ||||
|       before { Setting.peers_api_enabled = false } | ||||
| 
 | ||||
|       it 'returns http not found' do | ||||
|         get api_v1_instance_peers_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(404) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										18
									
								
								spec/requests/api/v1/instances/privacy_policies_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								spec/requests/api/v1/instances/privacy_policies_spec.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe 'Privacy Policy' do | ||||
|   describe 'GET /api/v1/instance/privacy_policy' do | ||||
|     it 'returns http success' do | ||||
|       get api_v1_instance_privacy_policy_path | ||||
| 
 | ||||
|       expect(response) | ||||
|         .to have_http_status(200) | ||||
| 
 | ||||
|       expect(body_as_json) | ||||
|         .to be_present | ||||
|         .and include(:content) | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										17
									
								
								spec/requests/api/v1/instances/rules_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								spec/requests/api/v1/instances/rules_spec.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe 'Rules' do | ||||
|   describe 'GET /api/v1/instance/rules' do | ||||
|     it 'returns http success' do | ||||
|       get api_v1_instance_rules_path | ||||
| 
 | ||||
|       expect(response) | ||||
|         .to have_http_status(200) | ||||
| 
 | ||||
|       expect(body_as_json) | ||||
|         .to be_an(Array) | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										43
									
								
								spec/requests/api/v1/instances/translation_languages_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								spec/requests/api/v1/instances/translation_languages_spec.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| describe 'Translation Languages' do | ||||
|   describe 'GET /api/v1/instances/translation_languages' do | ||||
|     context 'when no translation service is configured' do | ||||
|       it 'returns empty language matrix', :aggregate_failures do | ||||
|         get api_v1_instance_translation_languages_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(200) | ||||
| 
 | ||||
|         expect(body_as_json) | ||||
|           .to eq({}) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when a translation service is configured' do | ||||
|       before { configure_translation_service } | ||||
| 
 | ||||
|       it 'returns language matrix', :aggregate_failures do | ||||
|         get api_v1_instance_translation_languages_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(200) | ||||
| 
 | ||||
|         expect(body_as_json) | ||||
|           .to eq({ und: %w(en de), en: ['de'] }) | ||||
|       end | ||||
| 
 | ||||
|       private | ||||
| 
 | ||||
|       def configure_translation_service | ||||
|         allow(TranslationService).to receive_messages(configured?: true, configured: service_double) | ||||
|       end | ||||
| 
 | ||||
|       def service_double | ||||
|         instance_double(TranslationService::DeepL, languages: { nil => %w(en de), 'en' => ['de'] }) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										37
									
								
								spec/requests/api/v2/instance_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								spec/requests/api/v2/instance_spec.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'rails_helper' | ||||
| 
 | ||||
| describe 'Instances' do | ||||
|   let(:user)    { Fabricate(:user) } | ||||
|   let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id) } | ||||
|   let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } | ||||
| 
 | ||||
|   describe 'GET /api/v2/instance' do | ||||
|     context 'when logged out' do | ||||
|       it 'returns http success and json' do | ||||
|         get api_v2_instance_path | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(200) | ||||
| 
 | ||||
|         expect(body_as_json) | ||||
|           .to be_present | ||||
|           .and include(title: 'Mastodon') | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when logged in' do | ||||
|       it 'returns http success and json' do | ||||
|         get api_v2_instance_path, headers: headers | ||||
| 
 | ||||
|         expect(response) | ||||
|           .to have_http_status(200) | ||||
| 
 | ||||
|         expect(body_as_json) | ||||
|           .to be_present | ||||
|           .and include(title: 'Mastodon') | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue