Skip to content

Commit

Permalink
Merge branch 'development' of https://github.com/DMPRoadmap/roadmap i…
Browse files Browse the repository at this point in the history
…nto roadmap-main
  • Loading branch information
briri committed Dec 15, 2020
2 parents 3249f4c + 9a6e139 commit 95c2c2f
Show file tree
Hide file tree
Showing 97 changed files with 2,906 additions and 1,216 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/mysql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ jobs:
DB_ADAPTER: mysql2
MYSQL_PWD: root
RAILS_ENV: test
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}

steps:
# Checkout the repo
Expand Down Expand Up @@ -40,7 +39,6 @@ jobs:
cp config/database.yml.sample config/database.yml
cp config/initializers/contact_us.rb.example config/initializers/contact_us.rb
cp config/initializers/wicked_pdf.rb.example config/initializers/wicked_pdf.rb
cp config/credentials.yml.enc.workflow config/credentials.yml.enc
# Try to retrieve the gems from the cache
- name: 'Cache Gems'
Expand All @@ -57,6 +55,11 @@ jobs:
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3 --without pgsql rollbar aws
- name: 'Setup Credentials'
run: |
# generate a default credential file and key
EDITOR='echo "$(cat config/credentials.yml.example)" >' bundle exec rails credentials:edit
# Try to retrieve the yarn JS dependencies from the cache
- name: 'Cache Yarn Packages'
uses: actions/cache@v1
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/postgres.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ jobs:
env:
RAILS_ENV: test
DATABASE_URL: postgres://postgres:@localhost:5432/roadmap_test
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}

steps:
# Checkout the repo
Expand Down Expand Up @@ -57,7 +56,6 @@ jobs:
cp config/database.yml.sample config/database.yml
cp config/initializers/contact_us.rb.example config/initializers/contact_us.rb
cp config/initializers/wicked_pdf.rb.example config/initializers/wicked_pdf.rb
cp config/credentials.yml.enc.workflow config/credentials.yml.enc
# Try to retrieve the gems from the cache
- name: 'Cache Gems'
Expand All @@ -74,6 +72,11 @@ jobs:
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3 --without mysql rollbar aws
- name: 'Setup Credentials'
run: |
# generate a default credential file and key
EDITOR='echo "$(cat config/credentials.yml.example)" >' bundle exec rails credentials:edit
# Try to retrieve the yarn JS dependencies from the cache
- name: 'Cache Yarn Packages'
uses: actions/cache@v1
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![Actions Status](https://github.com/DMPRoadmap/roadmap/workflows/Tests%20-%20PostgreSQL/badge.svg)](https://github.com/DMPRoadmap/roadmap/actions)
[![Actions Status](https://github.com/DMPRoadmap/roadmap/workflows/Tests%20-%20MySQL/badge.svg)](https://github.com/DMPRoadmap/roadmap/actions)

DMP Roadmap is a Data Management Planning tool. Management and development of DMP Roadmap is jointly provided by the Digital Curation Centre (DCC), http://www.dcc.ac.uk/, and the University of California Curation Center (UC3), http://www.cdlib.org/services/uc3/
DMP Roadmap is a Data Management Planning tool. Management and development of DMP Roadmap is jointly provided by the Digital Curation Centre (DCC), http://www.dcc.ac.uk/, and the University of California Curation Center (UC3), http://www.cdlib.org/services/uc3/.

The tool has four main functions:

Expand All @@ -23,7 +23,7 @@ Roadmap is a Ruby on Rails application and you will need to have:
* Rails = 4.2
* MySQL >= 5.0 OR PostgreSQL

Further detail on how to install Ruby on Rails applications are available from the Ruby on Rails site: http://rubyonrails.org
Further detail on how to install Ruby on Rails applications are available from the Ruby on Rails site: http://rubyonrails.org.

Further details on how to install MySQL and create your first user and database. Be sure to follow the instructions for your particular environment.
* Install: http://dev.mysql.com/downloads/mysql/
Expand All @@ -36,10 +36,10 @@ You may also find the following resources handy:
* Ruby on Rails Tutorial Book: http://www.railstutorial.org/

#### Installation
See the [Installation Guide](https://github.com/DMPRoadmap/roadmap/wiki/Installation) on the Wiki
See the [Installation Guide](https://github.com/DMPRoadmap/roadmap/wiki/Installation) on the Wiki.

#### Troubleshooting
See the [Troubleshooting Guide](https://github.com/DMPRoadmap/roadmap/wiki/Troubleshooting) on the Wiki
See the [Troubleshooting Guide](https://github.com/DMPRoadmap/roadmap/wiki/Troubleshooting) on the Wiki.

#### Support
Issues should be reported here on [Github Issues](https://github.com/DMPRoadmap/roadmap/issues)
Expand All @@ -55,7 +55,7 @@ If you would like to contribute to the project. Please follow these steps to sub
* Then create a new Pull Request (PR) from your branch to this project's '_**development**_' branch in GitHub
* The project team will then review your PR and communicate with you to convey any additional changes that would ensure that your work adheres to our guidelines.

See the [Contribution Guide](https://github.com/DMPRoadmap/roadmap/blob/development/CONTRIBUTING.md) on the Wiki for more details
See the [Contribution Guide](https://github.com/DMPRoadmap/roadmap/blob/development/CONTRIBUTING.md) on the Wiki for more details.

#### License
The DMP Roadmap project uses the <a href="./LICENSE.md">MIT License</a>.
2 changes: 1 addition & 1 deletion app/controllers/api/v1/base_api_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def heartbeat

def render_error(errors:, status:)
@payload = { errors: [errors] }
render "/api/v1/error", status: status and return
render "/api/v1/error", status: status
end

private
Expand Down
167 changes: 82 additions & 85 deletions app/controllers/api/v1/plans_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,85 +9,74 @@ class PlansController < BaseApiController
respond_to :json

# GET /api/v1/plans/:id
# rubocop:disable Metrics/AbcSize
def show
plans = Plan.where(id: params[:id]).limit(1)

if plans.any?
if client.is_a?(User)
# If the specified plan does not belong to the org or the owner's org
if plans.first.org_id != client.org_id &&
plans.first.owner&.org_id != client.org_id

# Kaminari pagination requires an ActiveRecord resultset :/
plans = Plan.where(id: nil).limit(1)
end

elsif client.is_a?(ApiClient) && plans.first.api_client_id != client.id &&
!plans.first.publicly_visible?
# Kaminari pagination requires an ActiveRecord resultset :/
plans = Plan.where(id: nil).limit(1)
end

if plans.present? && plans.any?
@items = paginate_response(results: plans)
render "/api/v1/plans/index", status: :ok
else
render_error(errors: [_("Plan not found")], status: :not_found)
end
plans = Api::V1::PlansPolicy::Scope.new(client, Plan).resolve
.where(id: params[:id]).limit(1)

if plans.present? && plans.any?
@items = paginate_response(results: plans)
render "/api/v1/plans/index", status: :ok
else
render_error(errors: [_("Plan not found")], status: :not_found)
end
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable

# POST /api/v1/plans
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/BlockNesting
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def create
dmp = @json.with_indifferent_access.fetch(:items, []).first.fetch(:dmp, {})

# If a dmp_id was passed in try to find it
if dmp[:dmp_id].present? && dmp[:dmp_id][:identifier].present?
scheme = IdentifierScheme.by_name(dmp[:dmp_id][:type]).first
dmp_id = Identifier.where(value: dmp[:dmp_id][:identifier],
identifier_scheme: scheme)
end

# Skip if this is an existing DMP
if dmp_id.present?
render_error(errors: _("Plan already exists. Send an update instead."),
status: :bad_request)
# Do a pass through the raw JSON and check to make sure all required fields
# were present. If not, return the specific errors
errs = Api::V1::JsonValidationService.validation_errors(json: dmp)
render_error(errors: errs, status: :bad_request) and return if errs.any?

# Convert the JSON into a Plan and it's associations
plan = Api::V1::Deserialization::Plan.deserialize(json: dmp)
if plan.present?
save_err = _("Unable to create your DMP")
exists_err = _("Plan already exists. Send an update instead.")
no_org_err = _("Could not determine ownership of the DMP. Please add an
:affiliation to the :contact")

# Try to determine the Plan's owner
owner = determine_owner(client: client, plan: plan)
plan.org = owner.org if owner.present? && plan.org.blank?
render_error(errors: no_org_err, status: :bad_request) and return unless plan.org.present?

# Validate the plan and it's associations and return errors with context
# e.g. 'Contact affiliation name can't be blank' instead of 'name can't be blank'
errs = Api::V1::ContextualErrorService.process_plan_errors(plan: plan)

# The resulting plan (our its associations were invalid)
render_error(errors: errs, status: :bad_request) and return if errs.any?
# Skip if this is an existing DMP
render_error(errors: exists_err, status: :bad_request) and return unless plan.new_record?

# If we cannot save for some reason then return an error
plan = Api::V1::PersistenceService.safe_save(plan: plan)
# rubocop:disable Layout/LineLength
render_error(errors: save_err, status: :internal_server_error) and return if plan.new_record?

# rubocop:enable Layout/LineLength

# If the plan was generated by an ApiClient then associate them
plan.update(api_client_id: client.id) if client.is_a?(ApiClient)

# Invite the Owner if they are a Contributor then attach the Owner to the Plan
owner = invite_contributor(contributor: owner) if owner.is_a?(Contributor)
plan.add_user!(owner.id, :creator)

# Kaminari Pagination requires an ActiveRecord result set :/
@items = paginate_response(results: Plan.where(id: plan.id))
render "/api/v1/plans/index", status: :created
else
# Time prior to JSON parser service call which will create the plan so
# we can stop the creation of duplicate plans below
now = (Time.now - 1.minute)
plan = Api::V1::Deserialization::Plan.deserialize!(json: dmp)

if plan.present?
if plan.created_at.utc < now.utc
render_error(errors: _("Plan already exists. Send an update instead."),
status: :bad_request)

else
# If the plan was generated by an ApiClient then associate them
plan.update(api_client_id: client.id) if client.is_a?(ApiClient)

assign_roles(plan: plan)

# Kaminari Pagination requires an ActiveRecord result set :/
@items = paginate_response(results: Plan.where(id: plan.id))
render "/api/v1/plans/index", status: :created
end
else
render_error(errors: [_("Invalid JSON")], status: :bad_request)
end
render_error(errors: [_("Invalid JSON!")], status: :bad_request)
end
rescue JSON::ParserError
render_error(errors: [_("Invalid JSON")], status: :bad_request)
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/BlockNesting
# rubocop:enable
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength

# GET /api/v1/plans
def index
Expand All @@ -111,33 +100,42 @@ def dmp_params
params.require(:dmp).permit(plan_permitted_params).to_h
end

def assign_roles(plan:)
# Attach all of the authors and then invite them if necessary
owner = nil
plan.contributors.data_curation.each do |contributor|
user = contributor_to_user(contributor: contributor)
next unless user.present?

# Attach the role
role = Role.new(user: user, plan: plan)
role.creator = true if contributor.data_curation?
# We only want one owner/creator so jusst use the 1st contributor
# which should be the contact in the JSON input
owner = contributor if contributor.data_curation?
role.administrator = true if contributor.data_curation? &&
!contributor.present?
role.save
end
def plan_exists?(json:)
return false unless json.present? &&
json[:dmp_id].present? &&
json[:dmp_id][:identifier].present?

scheme = IdentifierScheme.by_name(json[:dmp_id][:type]).first
Identifier.where(value: json[:dmp_id][:identifier], identifier_scheme: scheme).any?
end

# Get the Plan's owner
def determine_owner(client:, plan:)
contact = plan.contributors.select(&:data_curation?).first
# Use the contact if it was sent in and has an affiliation defined
return contact if contact.present? && contact.org.present?

# If the contact has no affiliation defined, see if they are already a User
user = lookup_user(contributor: contact)
return user if user.present?

# Otherwise just return the client
client
end

# rubocop:disable Metrics/AbcSize
def contributor_to_user(contributor:)
def lookup_user(contributor:)
return nil unless contributor.present?

identifiers = contributor.identifiers.map do |id|
{ name: id.identifier_scheme&.name, value: id.value }
end
user = User.from_identifiers(array: identifiers) if identifiers.any?
user = User.find_by(email: contributor.email) unless user.present?
return user if user.present?
user
end

def invite_contributor(contributor:)
return nil unless contributor.present?

# If the user was not found, invite them and attach any know identifiers
names = contributor.name&.split || [""]
Expand All @@ -155,7 +153,6 @@ def contributor_to_user(contributor:)
end
user
end
# rubocop:enable Metrics/AbcSize

end

Expand Down
19 changes: 9 additions & 10 deletions app/controllers/orgs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ def admin_update
authorize @org
@org.logo = attrs[:logo] if attrs[:logo]
tab = (attrs[:feedback_enabled].present? ? "feedback" : "profile")
if attrs[:org_links].present?
@org.links = ActiveSupport::JSON.decode(attrs[:org_links])
attrs.delete(:org_links)
end
@org.links = ActiveSupport::JSON.decode(params[:org_links]) if params[:org_links].present?

# Only allow super admins to change the org types and shib info
if current_user.can_super_admin?
Expand Down Expand Up @@ -105,6 +102,7 @@ def shibboleth_ds
# Display the custom Shibboleth discovery service page.
@orgs = Identifier.by_scheme_name("shibboleth", "Org")
.sort { |a, b| a.identifiable.name <=> b.identifiable.name }
.map(&:identifiable)

# Disabling the rubocop check here because it would not be clear what happens
# if the ``@orgs` array has items ... it renders the shibboleth_ds view
Expand All @@ -120,10 +118,10 @@ def shibboleth_ds
# POST /orgs/shibboleth_ds
# rubocop:disable Metrics/AbcSize
def shibboleth_ds_passthru
if !shib_params["shib-ds"][:org_name].blank?
session["org_id"] = shib_params["shib-ds"][:org_name]
if !shib_params[:org_id].blank?
session["org_id"] = shib_params[:org_id]

org = Org.where(id: shib_params["shib-ds"][:org_id])
org = Org.where(id: shib_params[:org_id])
shib_entity = Identifier.by_scheme_name("shibboleth", "Org")
.where(identifiable: org)

Expand Down Expand Up @@ -211,14 +209,15 @@ def search
def org_params
params.require(:org)
.permit(:name, :abbreviation, :logo, :contact_email, :contact_name,
:remove_logo, :org_type, :managed, :feedback_enabled, :org_links,
:remove_logo, :managed, :feedback_enabled, :org_links,
:funder, :institution, :organisation,
:feedback_email_msg, :org_id, :org_name, :org_crosswalk,
identifiers_attributes: %i[identifier_scheme_id value],
tracker_attributes: %i[code])
tracker_attributes: %i[code id])
end

def shib_params
params.permit("shib-ds": %i[org_id org_name])
params.permit("org_id")
end

def search_params
Expand Down
6 changes: 6 additions & 0 deletions app/controllers/plan_exports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def show
format.text { show_text }
format.docx { show_docx }
format.pdf { show_pdf }
format.json { show_json }
end
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
Expand Down Expand Up @@ -91,6 +92,11 @@ def show_pdf
}
end

def show_json
json = render_to_string(partial: "/api/v1/plans/show", locals: { plan: @plan })
render json: "{\"dmp\":#{json}}"
end

def file_name
# Sanitize bad characters and replace spaces with underscores
ret = @plan.title
Expand Down
Loading

0 comments on commit 95c2c2f

Please sign in to comment.