diff --git a/Gemfile b/Gemfile index ce079fdbaa..06c6fabd24 100644 --- a/Gemfile +++ b/Gemfile @@ -213,6 +213,8 @@ gem 'httparty' # Autoload dotenv in Rails. (https://github.com/bkeepers/dotenv) gem 'dotenv-rails' +gem 'activerecord_json_validator' + # ================================= # # ENVIRONMENT SPECIFIC DEPENDENCIES # # ================================= # diff --git a/Gemfile.lock b/Gemfile.lock index d1a8d1dba1..0fdc757d62 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -33,6 +33,9 @@ GEM activemodel (= 5.2.6.2) activesupport (= 5.2.6.2) arel (>= 9.0) + activerecord_json_validator (2.1.0) + activerecord (>= 4.2.0, < 8) + json_schemer (~> 0.2.18) activestorage (5.2.6.2) actionpack (= 5.2.6.2) activerecord (= 5.2.6.2) @@ -130,6 +133,8 @@ GEM dragonfly-s3_data_store (1.3.0) dragonfly (~> 1.0) fog-aws + ecma-re-validator (0.4.0) + regexp_parser (~> 2.2) erubi (1.10.0) excon (0.91.0) execjs (2.8.1) @@ -207,6 +212,7 @@ GEM guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) + hana (1.3.7) hashdiff (1.0.1) hashie (5.0.0) highline (2.0.3) @@ -223,6 +229,11 @@ GEM actionview (>= 5.0.0) activesupport (>= 5.0.0) json (2.6.1) + json_schemer (0.2.19) + ecma-re-validator (~> 0.3) + hana (~> 1.3) + regexp_parser (~> 2.0) + uri_template (~> 0.7) jwt (2.3.0) kaminari (1.2.2) activesupport (>= 4.1.0) @@ -474,6 +485,7 @@ GEM thread_safe (~> 0.1) unicode-display_width (2.1.0) uniform_notifier (1.14.2) + uri_template (0.7.0) warden (1.2.9) rack (>= 2.0.9) web-console (3.7.0) @@ -515,6 +527,7 @@ PLATFORMS x86_64-linux DEPENDENCIES + activerecord_json_validator annotate annotate_gem api-pagination diff --git a/app/models/license.rb b/app/models/license.rb index 858b1360c9..aa09cdd144 100644 --- a/app/models/license.rb +++ b/app/models/license.rb @@ -49,4 +49,20 @@ class License < ApplicationRecord # Remove any preferred licenses that could not be found in the table licenses.compact } + + # varchar(255) NOT NULL + validates :name, + presence: { message: PRESENCE_MESSAGE }, + length: { in: 0..255, allow_nil: false } + + # varchar(255) NOT NULL + validates :identifier, + presence: { message: PRESENCE_MESSAGE }, + length: { in: 0..255, allow_nil: false } + + # varchar(255) NOT NULL + validates :uri, + presence: { message: PRESENCE_MESSAGE }, + length: { in: 0..255, allow_nil: false } + end diff --git a/app/models/metadata_standard.rb b/app/models/metadata_standard.rb index ea8f69c065..ef795f76cc 100644 --- a/app/models/metadata_standard.rb +++ b/app/models/metadata_standard.rb @@ -15,6 +15,36 @@ # rdamsc_id :string # class MetadataStandard < ApplicationRecord + + # ============= + # = Constants = + # ============= + + # keep "=>" syntax as json_schemer requires string keys + SCHEMA_RELATED_ENTITIES = { + "$schema" => "http://json-schema.org/draft-04/schema#", + "type" => "array", + "items" => { + "type" => "object", + "properties" => { + "id" => { "type" => "string" }, + "role" => { "type" => "string" } + } + } + } + + SCHEMA_LOCATIONS = { + "$schema" => "http://json-schema.org/draft-04/schema#", + "type" => "array", + "items" => { + "type" => "object", + "properties" => { + "url" => { "type" => "string" }, + "type" => { "type" => "string" } + } + } + } + # ================ # = Associations = # ================ @@ -29,4 +59,33 @@ class MetadataStandard < ApplicationRecord term = term.downcase where('LOWER(title) LIKE ?', "%#{term}%").or(where('LOWER(description) LIKE ?', "%#{term}%")) } + + # varchar(255) DEFAULT NULL + validates :title, + length: { maximum: 255 } + + # varchar(255) DEFAULT NULL + validates :rdamsc_id, + length: { maximum: 255 } + + # varchar(255) DEFAULT NULL + validates :uri, + length: { maximum: 255 } + + # json DEFAULT NULL + validates :related_entities, + json: { + schema: SCHEMA_RELATED_ENTITIES, + message: ->(errors) { errors } + }, + allow_nil: true + + # json DEFAULT NULL + validates :locations, + json: { + schema: SCHEMA_LOCATIONS, + message: ->(errors) { errors } + }, + allow_nil: true + end diff --git a/app/models/repository.rb b/app/models/repository.rb index 5db15716eb..e4e52a886a 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -22,6 +22,73 @@ # class Repository < ApplicationRecord + + # ============= + # = Constants = + # ============= + + # keep "=>" syntax as json_schemer requires string keys + SCHEMA_INFO = { + "$schema" => "http://json-schema.org/draft-04/schema#", + "type" => "object", + "properties" => { + "types" => { + "type" => "array", + "items" => { + "type" => "string" + } + }, + "keywords" => { + "type" => "array", + "items" => { + "type" => "string" + } + }, + "subjects" => { + "type" => "array", + "items" => { + "type" => "string" + } + }, + "access" => { + "type" => "string", + "enum" => ["open", "restricted", "closed"] + }, + "provider_types" => { + "type" => "array", + "items" => { + "type" => "string" + } + }, + "upload_types" => { + "type" => "array", + "items" => { + "type" => "object", + "properties" => { + "type" => { "type" => "string" }, + "restriction" => { "type" => "string" } + }, + "required" => ["type","restriction"] + } + }, + "policies" => { + "type" => "array", + "items" => { + "type" => "object", + "properties" => { + "name" => { "type" => "string" }, + "url" => { "type" => "string" } + }, + "required" => ["name","url"] + } + }, + "pid_system" => { + "type" => "string" + } + } + } + + # ================ # = Associations = # ================ @@ -51,4 +118,39 @@ class Repository < ApplicationRecord scope :by_facet, lambda { |facet| where(safe_json_where_clause(column: 'info', hash_key: 'keywords'), "%#{facet}%") } + + # =============== + # = Validations = + # =============== + + # varchar(255) NOT NULL + validates :name, + presence: { message: PRESENCE_MESSAGE }, + length: { in: 0..255, allow_nil: false } + + # text NOT NULL + validates :description, + presence: { message: PRESENCE_MESSAGE } + + # varchar(255) NOT NULL + validates :uri, + presence: { message: PRESENCE_MESSAGE }, + length: { in: 0..255, allow_nil: false } + + # varchar(255) DEFAULT NULL + validates :homepage, + length: { maximum: 255 } + + # varchar(255) DEFAULT NULL + validates :contact, + length: { maximum: 255 } + + # json DEFAULT NULL + validates :info, + json: { + schema: SCHEMA_INFO, + message: ->(errors) { errors } + }, + allow_nil: true + end