diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7252fef37..411e2f9b6 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,5 @@ class ApplicationController < ActionController::API + include ActionController::HttpAuthentication::Basic::ControllerMethods include Authenticable include CanCan::ControllerAdditions include ErrorSerializable @@ -52,6 +53,16 @@ def default_format_json request.format = :json if request.format.html? end + def authenticate_user_with_basic_auth! + @user = authenticate_user! + + if !@user + request_http_basic_authentication(realm = ENV['REALM']) + end + + @user + end + def authenticate_user! type, credentials = type_and_credentials_from_request_headers return false unless credentials.present? @@ -83,7 +94,7 @@ def authenticated_user when "ActionController::UnknownFormat" then 406 when "ActiveRecord::RecordNotUnique" then 409 when "ActiveModel::ForbiddenAttributesError", "ActionController::ParameterMissing", "ActionController::UnpermittedParameters", "ActiveModelSerializers::Adapter::JsonApi::Deserialization::InvalidDocument" then 422 - when "SocketError" then 500 + when "SocketError" then 500 else 400 end @@ -135,7 +146,7 @@ def set_raven_context else Raven.user_context( ip_address: request.ip - ) + ) end end end diff --git a/app/controllers/dois_controller.rb b/app/controllers/dois_controller.rb index 2bd08c2d3..f5a758c5a 100644 --- a/app/controllers/dois_controller.rb +++ b/app/controllers/dois_controller.rb @@ -53,6 +53,7 @@ def index else response = Doi.query(params[:query], state: params[:state], + exclude_registration_agencies: params[:exclude_registration_agencies], created: params[:created], registered: params[:registered], provider_id: params[:provider_id], diff --git a/app/controllers/export_controller.rb b/app/controllers/export_controller.rb new file mode 100644 index 000000000..3c8c85660 --- /dev/null +++ b/app/controllers/export_controller.rb @@ -0,0 +1,281 @@ +class ExportController < ApplicationController + include ActionController::MimeResponds + + before_action :authenticate_user_with_basic_auth! + + def contacts + authorize! :export, :contacts + + begin + # Loop through all providers + providers = [] + + page = { size: 1000, number: 1} + response = Provider.query(nil, page: page, include_deleted: true) + providers = providers + response.records.to_a + + total = response.results.total + total_pages = page[:size] > 0 ? (total.to_f / page[:size]).ceil : 0 + + # keep going for all pages + page_num = 2 + while page_num <= total_pages + page = { size: 1000, number: page_num } + response = Provider.query(nil, page: page) + providers = providers + response.records.to_a + page_num += 1 + end + + respond_to do |format| + format.csv do + headers = %W( + fabricaAccountId + email + firstName + lastName + type + ) + + csv = headers.to_csv + + providers.each do |provider| + + csv += CSV.generate_line [ + provider.symbol, + provider.technical_contact_email, + provider.technical_contact_given_name, + provider.technical_contact_family_name, + 'technical' + ] + csv += CSV.generate_line [ + provider.symbol, + provider.secondary_technical_contact_email, + provider.secondary_technical_contact_given_name, + provider.secondary_technical_contact_family_name, + 'secondaryTechnicalContact' + ] + csv += CSV.generate_line [ + provider.symbol, + provider.service_contact_email, + provider.service_contact_given_name, + provider.service_contact_family_name, + 'service' + ] + csv += CSV.generate_line [ + provider.symbol, + provider.secondary_service_contact_email, + provider.secondary_service_contact_given_name, + provider.secondary_service_contact_family_name, + 'secondaryService' + ] + csv += CSV.generate_line [ + provider.symbol, + provider.voting_contact_email, + provider.voting_contact_given_name, + provider.voting_contact_family_name, + 'voting' + ] + csv += CSV.generate_line [ + provider.symbol, + provider.billing_contact_email, + provider.billing_contact_given_name, + provider.billing_contact_family_name, + 'billing' + ] + csv += CSV.generate_line [ + provider.symbol, + provider.secondary_billing_contact_email, + provider.secondary_billing_contact_given_name, + provider.secondary_billing_contact_family_name, + 'secondaryBilling' + ] + + end + + send_data csv, filename: "contacts-#{Date.today}.csv" + end + end + + rescue Elasticsearch::Transport::Transport::Errors::BadRequest => exception + Raven.capture_exception(exception) + + message = JSON.parse(exception.message[6..-1]).to_h.dig("error", "root_cause", 0, "reason") + + render json: { "errors" => { "title" => message }}.to_json, status: :bad_request + end + end + + def organizations + authorize! :export, :organizations + begin + # Loop through all providers + providers = [] + + page = { size: 1000, number: 1} + response = Provider.query(nil, page: page, include_deleted: true) + providers = providers + response.records.to_a + + total = response.results.total + total_pages = page[:size] > 0 ? (total.to_f / page[:size]).ceil : 0 + + # keep going for all pages + page_num = 2 + while page_num <= total_pages + page = { size: 1000, number: page_num } + response = Provider.query(nil, page: page) + providers = providers + response.records.to_a + page_num += 1 + end + + # Loop through all clients + clients = [] + + page = { size: 1000, number: 1} + response = Client.query(nil, page: page, include_deleted: true) + clients = clients + response.records.to_a + + total = response.results.total + total_pages = page[:size] > 0 ? (total.to_f / page[:size]).ceil : 0 + + # keep going for all pages + page_num = 2 + while page_num <= total_pages + page = { size: 1000, number: page_num } + response = Client.query(nil, page: page) + clients = clients + response.records.to_a + page_num += 1 + end + + # Get doi counts via DOIS query and combine next to clients. + response = Doi.query(nil, state: "registered,findable", page: { size: 0, number: 1}, totals_agg: true) + + client_totals = {} + totals_buckets = response.response.aggregations.clients_totals.buckets + totals_buckets.each do |totals| + client_totals[totals["key"]] = { + "count" => totals["doc_count"], + "this_year" => totals.this_year.buckets[0]["doc_count"], + "last_year" => totals.last_year.buckets[0]["doc_count"] + } + end + + respond_to do |format| + format.csv do + headers = %W( + accountName + fabricaAccountId + parentFabricaAccountId + salesForceId + parentSalesForceId + isActive + accountDescription + accountWebsite + region + focusArea + organisationType + accountType + generalContactEmail + groupEmail + billingStreet + billingPostalCode + billingCity + billingDepartment + billingOrganization + billingState + billingCountry + twitter + rorId + created + deleted + doisCountCurrentYear + doisCountPreviousYear + doisCountTotal + ) + + csv = headers.to_csv + + providers.each do |provider| + row = { + accountName: provider.name, + fabricaAccountId: provider.symbol, + parentFabricaAccountId: provider.consortium.present? ? provider.consortium.symbol : nil, + salesForceId: provider.salesforce_id, + parentSalesForceId: provider.consortium.present? ? provider.consortium.salesforce_id : nil, + isActive: provider.is_active == "\x01", + accountDescription: provider.description, + accountWebsite: provider.website, + region: provider.region_human_name, + focusArea: provider.focus_area, + organizationType: provider.organization_type, + accountType: provider.member_type_label, + generalContactEmail: provider.system_email, + groupEmail: provider.group_email, + billingStreet: provider.billing_address, + billingPostalCode: provider.billing_post_code, + billingCity: provider.billing_city, + billingDepartment: provider.billing_department, + billingOrganization: provider.billing_organization, + billingState: provider.billing_state, + billingCountry: provider.billing_country, + twitter: provider.twitter_handle, + rorId: provider.ror_id, + created: provider.created, + deleted: provider.deleted_at, + doisCountCurrentYear: nil, + doisCountPreviousYear: nil, + doisCountTotal: nil + }.values + + csv += CSV.generate_line row + end + + clients.each do |client| + client_id = client.symbol.downcase + row = { + accountName: client.name, + fabricaAccountId: client.symbol, + parentFabricaAccountId: client.provider.present? ? client.provider.symbol : nil, + salesForceId: client.salesforce_id, + parentSalesForceId: client.provider.present? ? client.provider.salesforce_id : nil, + isActive: client.is_active == "\x01", + accountDescription: client.description, + accountWebsite: client.url, + region: nil, + focusArea: nil, + organisztionType: nil, + accountType: 'repository', + generalContactEmail: client.system_email, + groupEmail: nil, + billingStreet: nil, + billingPostalCode: nil, + billingCity: nil, + billingDepartment: nil, + billingOrganization: nil, + billingState: nil, + billingCountry: nil, + twitter: nil, + rorId: nil, + created: client.created, + deleted: client.deleted_at, + doisCountCurrentYear: client_totals[client_id] ? client_totals[client_id]["this_year"] : nil, + doisCountPreviousYear: client_totals[client_id] ? client_totals[client_id]["last_year"] : nil, + doisCountTotal: client_totals[client_id] ? client_totals[client_id]["count"] : nil + }.values + + csv += CSV.generate_line row + end + + send_data csv, filename: "organizations-#{Date.today}.csv" + end + end + + rescue Elasticsearch::Transport::Transport::Errors::BadRequest => exception + Raven.capture_exception(exception) + + message = JSON.parse(exception.message[6..-1]).to_h.dig("error", "root_cause", 0, "reason") + + render json: { "errors" => { "title" => message }}.to_json, status: :bad_request + end + end + +end \ No newline at end of file diff --git a/app/models/ability.rb b/app/models/ability.rb index 411e04f8c..542a55aa8 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -14,6 +14,8 @@ def initialize(user) cannot [:new, :create], Doi do |doi| doi.client.blank? || !(doi.client.prefixes.where(prefix: doi.prefix).first || %w(crossref medra kisti jalc op).include?(doi.client.symbol.downcase.split(".").first)) end + can :export, :contacts + can :export, :organizations elsif user.role_id == "staff_user" can :read, :all elsif user.role_id == "provider_admin" && user.provider_id.present? @@ -64,7 +66,7 @@ def initialize(user) # end can [:read, :destroy, :update, :register_url, :validate, :undo, :get_url, :get_urls, :read_landing_page_results], Doi, :client_id => user.client_id - can [:new, :create], Doi do |doi| + can [:new, :create], Doi do |doi| doi.client.prefixes.where(prefix: doi.prefix).present? || %w(crossref medra kisti jalc op).include?(doi.client.symbol.downcase.split(".").first) end can [:read], Doi do |doi| diff --git a/config/application.rb b/config/application.rb index 516a1dbfc..3c35f52e2 100644 --- a/config/application.rb +++ b/config/application.rb @@ -56,6 +56,7 @@ ENV['MG_FROM'] ||= "support@datacite.org" ENV['MG_DOMAIN'] ||= "mg.datacite.org" ENV['HANDLES_MINTED'] ||= "10132" +ENV['REALM'] ||= ENV['API_URL'] module Lupo class Application < Rails::Application diff --git a/config/routes.rb b/config/routes.rb index 7b5916e06..ca9a6d55a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -57,6 +57,11 @@ get 'clients/totals', :to => 'clients#totals' get 'prefixes/totals', :to => 'prefixes#totals' + # Reporting + get 'export/organizations', :to => 'export#organizations', defaults: { format: :csv } + get 'export/contacts', :to => 'export#contacts', defaults: { format: :csv } + + resources :heartbeat, only: [:index] resources :index, only: [:index]