Skip to content

Commit

Permalink
add 1.0.0 client libraries and update README
Browse files Browse the repository at this point in the history
  • Loading branch information
quoideneuf committed Oct 14, 2013
1 parent c3b8b1a commit 4246037
Show file tree
Hide file tree
Showing 91 changed files with 5,549 additions and 8 deletions.
40 changes: 32 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
Archon2ArchivesSpace README
================
# System Requirements

You will need to have Ruby 1.9.3 installed to run this service

ruby --version
# example output: ruby 1.9.3p429 (2013-05-15 revision 40747)

If your system has a different version of Ruby installed, the simplest way to
leave your system intact and get 1.9.3 is to install RVM (https://rvm.io/).

# Installing the service

Checkout the project from Github:
Download a release or just checkout the project from Github:

git clone https://github.com/lcdhoffman/archon-migration.git
cd archon-migration

Run a script to download the necessary ArchivesSpace libraries:

./scripts/import\_client\_libs.sh v1.0.0RC1
./scripts/import\_client\_libs.sh v1.0.0RC1

This will attempt to download the ArchivesSpace source code for ArchivesSpace v1.0.0RC1.
*Note: the service ships with libraries for ArchivesSpace 1.0.0, so you can skip this step
if you are targeting 1.0.0.

Install the gems in the Gemfile:
Install the application dependencies listed in the Gemfile:

gem install bundler
bundle install
Expand All @@ -22,7 +34,17 @@ Now run the application:

ruby app/main.rb

(You'll probably want to daemonize or disown this.)
The service runs on port 4568 by default. To change this:

touch config/config_local.rb
echo "Appdata.port_number YOUR_FAVORITE_PORT_HERE" >> config/config_local.rb

# Daemonizing the Service

The service can be daemonized in several ways. One option is to install a native
ruby solution such as the Daemonize gem (http://daemons.rubyforge.org/). However,
since the service is intended to be short-lived, it may be easiest to simply
send the process to the background and disown it.

# Using the Service

Expand All @@ -40,10 +62,10 @@ The best way to configure the application is to create a local config file:

touch config/config_local.rb

To change, for example, the port that application runs on, add the following
To change, for example, the version of the ArchivesSpace target, add the following
line

Appdata.port_number 4568
Appdata.aspace_version 'v1.0.1'

# Notes

Expand All @@ -60,10 +82,12 @@ contains data.

Do not allow Archon users to create or edit data while the migration is running.

Do not allow ArchivesSpace users to create or edit data while the migration is
running.

You can optimize the performance of the migration tool by adjusting the number of
pages of Archon data that are cached. For example, if your largest Archon collection contains 50,000 Content records, and you are running the migration tool in an environment that can afford around 300MB of memory, you might want to add this line to your config_local.rb file:

Appdata.archon_page_cache_size 500

There's no (or little) advantage to setting the cache size to a value larger than the number of Content records in the largest Collection, divided by 100.

There's no (or little) advantage to setting the page cache size to a value larger than the number of Content records in the largest Collection, divided by 100. There is a significant disadvantage to keeping your page cache size smaller than the number of pages of items in your largest collection.
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
require 'date'

# Add a new 'ifmissing' attribute which emits either an error or warning
# depending on its value.
class IfMissingAttribute < JSON::Schema::PropertiesAttribute

def self.validate(current_schema, data, fragments, validator, options = {})
super

if data.is_a?(Hash)
current_schema.schema['properties'].each do |property, property_schema|
if (property_schema['ifmissing'] && !data.has_key?(property))
message = nil

if property_schema['ifmissing'] == 'error'
message = "ERROR: The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
elsif property_schema['ifmissing'] == 'warn'
message = "WARNING: The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
end

if message
validation_error(message, fragments, current_schema, self, options[:record_errors])
end
end
end
end
end

end


class ArchivesSpaceTypeAttribute < JSON::Schema::TypeAttribute
extend JSONModel


# This reuse business is a bit of a pain. The story here: JRuby backtraces
# are relatively expensive to create (relative to MRI Ruby), and JSON Schema
# validation is using exceptions as control flow here (sigh). During imports,
# these validation error are hit a *lot*, and calculating a backtrace every
# time is expensive.
#
# So, we recycle.
def self.validation_error_for(expected_type, fragments, current_schema)
Thread.current[:json_validation_cached_errors] ||= {}
if !Thread.current[:json_validation_cached_errors][expected_type]
msg = "ERROR: Schema type mismatch. Expected type: #{expected_type}"
Thread.current[:json_validation_cached_errors][expected_type] = JSON::Schema::ValidationError.new(msg, fragments, self, current_schema)
end

Thread.current[:json_validation_cached_errors][expected_type].fragments = fragments
Thread.current[:json_validation_cached_errors][expected_type]
end


def self.validate(current_schema, data, fragments, validator, options = {})
types = current_schema.schema['type']

if types == 'date'
begin
Date.parse(data)
return
rescue
validation_error("The property '#{build_fragment(fragments)}' was not " +
"a well-formed date (value: #{data})",
fragments, current_schema, self, options[:record_errors])
end
end


if types == 'object' && data.is_a?(Hash) && data.has_key?('ref') && current_schema.schema['subtype'] != 'ref'
# Provide a helpful warning about potentially missing subtype definitions
$stderr.puts("WARNING: Schema #{current_schema.inspect} appears to be missing a subtype definition of 'ref'")
end

# A bit crazy, sorry. If we're being asked to validate a hash whose
# jsonmodel_type is marked against a different JSONModel schema, we're
# wasting our time. Just stop straight away.
if (data.is_a?(Hash) && data["jsonmodel_type"]) &&
(current_schema.schema.is_a?(Hash) &&
"#{current_schema.schema["type"]}".include?("JSONModel") &&
!"#{current_schema.schema["type"]}".include?("JSONModel(:#{data['jsonmodel_type']})"))

raise validation_error_for(data['jsonmodel_type'], fragments, current_schema)
end

if JSONModel.parse_jsonmodel_ref(types)
(model, qualifier) = JSONModel.parse_jsonmodel_ref(types)

if qualifier == 'uri' || (qualifier == 'uri_or_object' && data.is_a?(String))
if JSONModel(model).id_for(data, {}, true).nil?
validation_error("The property '#{build_fragment(fragments)}' of type " +
"#{data.class} did not match the following type: #{types} in schema",
fragments, current_schema, self, options[:record_errors])
end

elsif qualifier == 'uri_or_object' || qualifier == 'object'
if data.is_a?(Hash)
data["jsonmodel_type"] ||= model.to_s

ValidatorCache.with_validator_for(JSONModel(model), data) do |subvalidator|
# Urk. Validate the subrecord but pass in the fragments of the point
# we're at in the parent record.
subvalidator.instance_eval do
@base_schema.validate(@data, fragments, @validation_options)
end
end

else
validation_error("The property '#{build_fragment(fragments)}' of type " +
"#{data.class} did not match the following type: #{types} in schema",
fragments, current_schema, self, options[:record_errors])
end
end
else
super
end
end
end


class ArchivesSpaceSubTypeAttribute < JSON::Schema::TypeAttribute

def self.validate(current_schema, data, fragments, validator, options = {})
if data.is_a?(Hash) && !data.has_key?('ref')
message = "ERROR: The property '#{build_fragment(fragments)}' did not contain a required property of 'ref'"
validation_error(message, fragments, current_schema, self, options[:record_errors])
end
end

end


class ArchivesSpaceDynamicEnumAttribute < JSON::Schema::TypeAttribute

def self.validate(current_schema, data, fragments, validator, options = {})
enum_name = current_schema.schema['dynamic_enum']

if !JSONModel.init_args[:enum_source].valid?(enum_name, data)
possible_values = JSONModel.init_args[:enum_source].values_for(enum_name)

message = ("The property '#{build_fragment(fragments)}' value #{data.inspect} " +
"did not match one of the following configurable values: #{possible_values.join(', ')}")
validation_error(message, fragments, current_schema, self, options[:record_errors])
end
end

end


class ArchivesSpaceSchema < JSON::Schema::Validator
def initialize
super
extend_schema_definition("http://json-schema.org/draft-03/schema#")
@attributes["type"] = ArchivesSpaceTypeAttribute
@attributes["subtype"] = ArchivesSpaceSubTypeAttribute
@attributes["dynamic_enum"] = ArchivesSpaceDynamicEnumAttribute
@attributes["properties"] = IfMissingAttribute
@uri = URI.parse("http://www.archivesspace.org/archivesspace.json")
end


def already_failed?(fragments)
JSON::Validator.validation_errors.any? {|error|
error.fragments == fragments
}
end


def validate(current_schema, data, fragments, options = {})
super

# Run any custom validations if we've made it this far with no errors
if !already_failed?(fragments) && current_schema.schema.has_key?("validations")
current_schema.schema["validations"].each do |level_and_name|
level, name = level_and_name

errors = JSONModel::custom_validations[name].call(data)

errors.each do |error|
error_string = nil

if error.is_a? Symbol
error_string = "Validation error code: #{error}"
else
field, msg = error
prefix = level == :warning ? "Warning generated for" : "Validation failed for"
error_string = "#{prefix} '#{field}': #{msg}"

end

err = JSON::Schema::ValidationError.new(error_string,
fragments,
"custom_validation",
current_schema)

JSON::Validator.validation_error(err)
end
end
end
end

JSON::Validator.register_validator(self.new)
end
Loading

0 comments on commit 4246037

Please sign in to comment.