LP#1729620 Cleanup, fix bugs
authorRemington Steed <rjs7@calvin.edu>
Thu, 2 Nov 2017 14:14:51 +0000 (15:14 +0100)
committerJane Sandberg <sandbergja@gmail.com>
Mon, 28 Mar 2022 02:57:21 +0000 (19:57 -0700)
  - Rename DB schema file to follow convention
    - Remove optional DB commands, and leave them in Release Notes (and
      possibly add to Official Docs)
  - Create DB upgrade script
    - Print optional DB commands using \qecho
  - Change variable "tcn" to "rec_id" everywhere
  - Move perl API registration to be immediately after related function
  - Remove unused parameter from sub oai_list_retrieve() in
    Application/OAI.pm, and from API calls in WWW/OAI.pm
  - Fix mislabeled parameter in API doc
  - Add missing end-comment tags in opensrf.xml.example
  - Add dependency to Ubuntu/Debian makefiles
  - Add missing init handler in apache/eg.conf.in
  - Fix reference to sysconfdir in apache/eg_startup.in
  - Undo extraneous change to .gitignore
  - Trim/rename release notes, since most of the install process is
    handled via Evergreen install instructions.

Signed-off-by: Remington Steed <rjs7@calvin.edu>
Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Signed-off-by: Mike Rylander <mrylander@gmail.com>

12 files changed:
.gitignore
Open-ILS/examples/fm_IDL.xml
Open-ILS/examples/opensrf.xml.example
Open-ILS/src/extras/install/Makefile.debian-buster
Open-ILS/src/extras/install/Makefile.debian-stretch
Open-ILS/src/perlmods/lib/OpenILS/Application/OAI.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/OAI.pm
Open-ILS/src/sql/Pg/600.schema.oai.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/oai.sql [deleted file]
Open-ILS/src/sql/Pg/upgrade/XXXX.schema.oai_views.sql [new file with mode: 0644]
docs/RELEASE_NOTES_NEXT/OAI2/install.adoc [deleted file]
docs/RELEASE_NOTES_NEXT/OAI2/new_oai_opensrf_service.adoc [new file with mode: 0644]

index b49215d..23d7d99 100644 (file)
@@ -5,7 +5,6 @@
 *.pyc
 *.slo
 *.class
-.idea/
 
 aclocal.m4
 autom4te.cache/
index 4880eee..878ea54 100644 (file)
@@ -79,7 +79,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                   oils_persist:readonly="true" reporter:core="false" reporter:label="OAI2 record list"
                   oils_persist:tablename="oai.biblio">
                <fields>
-                       <field reporter:label="TCN Value\OAI identifier postfix" name="tcn" reporter:datatype="number"/>
+                       <field reporter:label="Record ID\OAI identifier postfix" name="rec_id" reporter:datatype="number"/>
                        <field reporter:label="Last edit date\OAI datestamp" name="datestamp" reporter:datatype="timestamp"/>
                        <field reporter:label="Is Deleted?" name="deleted" reporter:datatype="bool"/>
                        <field reporter:label="Setspec" name="set_spec" oils_persist:virtual="true"/>
@@ -89,7 +89,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                   oils_persist:readonly="true" reporter:core="false" reporter:label="OAI2 record list"
                   oils_persist:tablename="oai.authority">
                <fields>
-                       <field reporter:label="TCN Value\OAI identifier postfix" name="tcn" reporter:datatype="number"/>
+                       <field reporter:label="Record ID\OAI identifier postfix" name="rec_id" reporter:datatype="number"/>
                        <field reporter:label="Last edit date\OAI datestamp" name="datestamp" reporter:datatype="timestamp"/>
                        <field reporter:label="Is Deleted?" name="deleted" reporter:datatype="bool"/>
                        <field reporter:label="Setspec" name="set_spec" oils_persist:virtual="true"/>
index 33e14e7..a76e65b 100644 (file)
@@ -1087,6 +1087,7 @@ vim:et:ts=4:sw=4:
                     <!-- Accept only 852$[barcode] values that match this regular expression. E.g.
                     <barcode_filter>^[A-Za-z0-9]+</barcode_filter>
                     only renders 852 datafields that contain barcodes values that begin with letters and numbers.
+                    -->
                     <!--
                     <barcode_filter><barcode_filter>
                     -->
@@ -1094,6 +1095,7 @@ vim:et:ts=4:sw=4:
                     <!-- Accept only 852$[status] values that match this regular expression. E.g.
                     <status_filter>^Available$</status_filter>
                     only renders 852 datafields that contain status code values that exactly match the string 'Available'.
+                    -->
                     <!--
                     <status_filter></status_filter>
                     -->
index 9d53ca6..68b9480 100644 (file)
@@ -42,6 +42,7 @@ export DEBS = \
        libexcel-writer-xlsx-perl\
        libgd-graph3d-perl\
        libgeo-coder-osm-perl\
+       libhttp-oai-perl\
        liblocale-maketext-lexicon-perl\
        liblog-log4perl-perl\
        libmarc-charset-perl \
index f802314..d242aa6 100644 (file)
@@ -42,6 +42,7 @@ export DEBS = \
        libexcel-writer-xlsx-perl\
        libgd-graph3d-perl\
        libgeo-coder-osm-perl\
+       libhttp-oai-perl\
        liblocale-maketext-lexicon-perl\
        liblog-log4perl-perl\
        libmarc-charset-perl \
index c4fb61a..3605167 100644 (file)
@@ -161,7 +161,7 @@ sub oai_biblio_retrieve {
 
     my $self = shift;
     my $client = shift;
-    my $tcn = shift;
+    my $rec_id = shift;
     my $metadataPrefix = shift;
 
     #  holdings hold an array of call numbers, which hold an array of copies
@@ -173,7 +173,7 @@ sub oai_biblio_retrieve {
     # Retrieve the bibliographic record and it's copies
     my $tree = $_storage->request(
         "open-ils.cstore.direct.biblio.record_entry.retrieve",
-        $tcn,
+        $rec_id,
         { flesh     => 5,
           flesh_fields  => {
                     bre => [qw/marc edit_date call_numbers/],
@@ -190,7 +190,7 @@ sub oai_biblio_retrieve {
     my %serials;
     if ( substr($marc->leader, 7, 1) eq 's' ) { # serial
         my $_search = OpenSRF::AppSession->create( 'open-ils.search' );
-        my $_serials = $_search->request('open-ils.search.serial.record.bib.retrieve', $tcn, 1, 0)->gather(1);
+        my $_serials = $_search->request('open-ils.search.serial.record.bib.retrieve', $rec_id, 1, 0)->gather(1);
         my $order = 0 ;
         for my $sre (@$_serials) {
             if ( $sre->location ) {
@@ -269,7 +269,7 @@ sub oai_biblio_retrieve {
         $marc->delete_field($_) for ($marc->field('001'));
         if (!$marc->field('001')) {
             $marc->insert_fields_ordered(
-                MARC::Field->new( '001', $tcn )
+                MARC::Field->new( '001', $rec_id )
             );
         }
 
@@ -302,6 +302,36 @@ sub oai_biblio_retrieve {
 }
 
 
+__PACKAGE__->register_method(
+    method    => 'oai_biblio_retrieve',
+    api_name  => 'open-ils.oai.biblio.retrieve',
+    api_level => 1,
+    argc      => 1,
+    signature =>
+    {
+        desc     => 'Returns the MARCXML representation of the requested bibliographic record.',
+        params   =>
+        [
+            {
+                name => 'rec_id',
+                desc => 'An OpenILS biblio::record_entry id.',
+                type => 'number'
+            },
+            {
+                name => 'metadataPrefix',
+                desc => 'The metadataPrefix of the schema.',
+                type => 'string'
+            }
+        ],
+        'return' =>
+        {
+            desc => 'An string of the XML in the desired schema.',
+            type => 'string'
+        }
+    }
+);
+
+
 sub most_recent_date {
 
     my $date1 = substr(shift, 0, 19) ;  # e.g. '2001-02-03T04:05:06+0000' becomes '2001-02-03T04:05:06'
@@ -338,47 +368,18 @@ sub _cp_is_visible {
     return $visible;
 }
 
-__PACKAGE__->register_method(
-    method    => 'oai_biblio_retrieve',
-    api_name  => 'open-ils.oai.biblio.retrieve',
-    api_level => 1,
-    argc      => 1,
-    signature =>
-    {
-        desc     => 'Returns the MARCXML representation of the requested bibliographic record.',
-        params   =>
-        [
-            {
-                name => 'tcn',
-                desc => 'An OpenILS biblio::record_entry id.',
-                type => 'number'
-            },
-            {
-                name => 'metadataPrefix',
-                desc => 'The metadataPrefix of the schema.',
-                type => 'string'
-            }
-        ],
-        'return' =>
-        {
-            desc => 'An string of the XML in the desired schema.',
-            type => 'string'
-        }
-    }
-);
-
 
 sub oai_authority_retrieve {
 
     my $self = shift;
     my $client = shift;
-    my $tcn = shift;
+    my $rec_id = shift;
     my $metadataPrefix = shift;
 
     my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
 
     # Retrieve the authority record
-    my $record = $_storage->request('open-ils.cstore.direct.authority.record_entry.retrieve', $tcn)->gather(1);
+    my $record = $_storage->request('open-ils.cstore.direct.authority.record_entry.retrieve', $rec_id)->gather(1);
     my $o = Fieldmapper::authority::record_entry->new($record) ;
     my $marc = MARC::Record->new_from_xml( $o->marc, 'UTF8', 'XML');
 
@@ -386,7 +387,7 @@ sub oai_authority_retrieve {
     $marc->delete_field($_) for ($marc->field('001'));
     if (!$marc->field('001')) {
         $marc->insert_fields_ordered(
-            MARC::Field->new( '001', $tcn )
+            MARC::Field->new( '001', $rec_id )
         );
     }
 
@@ -409,7 +410,7 @@ __PACKAGE__->register_method(
         params   =>
         [
             {
-                name => 'tcn',
+                name => 'rec_id',
                 desc => 'An OpenILS authority::record_entry id.',
                 type => 'number'
             },
@@ -433,16 +434,15 @@ sub oai_list_retrieve {
     my $self            = shift;
     my $client          = shift;
     my $record_class    = shift || 'biblio';
-    my $tcn             = shift || 0;
+    my $rec_id          = shift || 0;
     my $from            = shift;
     my $until           = shift;
     my $set             = shift ;
-    my $metadataPrefix  = shift;
     my $max_count       = shift;
     my $deleted_record  = shift || 'yes';
 
     my $query = {};
-    $query->{'tcn'}       = ($max_count eq 1) ? $tcn : {'>=' => $tcn} ;
+    $query->{'rec_id'}    = ($max_count eq 1) ? $rec_id : {'>=' => $rec_id} ;
     $query->{'set_spec'}  = $set                     if ( $set ); # unsupported
     $query->{'deleted'}   = 'f'                      unless ( $deleted_record eq 'yes' );
     $query->{'datestamp'} = {'>=', $from}            if ( $from && !$until ) ;
@@ -473,8 +473,8 @@ __PACKAGE__->register_method(
                 desc => '\'biblio\' for bibliographic records or \'authority\' for authority records',
                 type => 'string'
             },            {
-                name => 'tcn',
-                desc => 'An optional tcn number used as a cursor.',
+                name => 'rec_id',
+                desc => 'An optional rec_id number used as a cursor.',
                 type => 'number'
             },
             {
@@ -493,13 +493,8 @@ __PACKAGE__->register_method(
                 type => 'string'
             },
             {
-                name => 'metadataPrefix',
-                desc => 'The metadataPrefix of the schema.',
-                type => 'string'
-            },
-            {
-                name => 'offset',
-                desc => 'The start of the cursor position in the result set.',
+                name => 'max_count',
+                desc => 'The number of identifiers to return.',
                 type => 'number'
             },
             {
@@ -517,4 +512,4 @@ __PACKAGE__->register_method(
 );
 
 
-1;
\ No newline at end of file
+1;
index 7fc5f36..448847e 100644 (file)
@@ -281,10 +281,10 @@ sub getRecord {
     # Do we have a valid identifier ?
     my $regex_identifier = "^${scheme}${delimiter}${repository_identifier}${delimiter}([0-9]+)\$";
     if ( $identifier =~ /$regex_identifier/i ) {
-        my $tcn = $1 ;
+        my $rec_id = $1 ;
 
         # Do we have a record ?
-        my $record = $oai->request('open-ils.oai.list.retrieve', $record_class, $tcn, undef, undef, undef, $metadataPrefix, 1, $deleted_record)->gather(1) ;
+        my $record = $oai->request('open-ils.oai.list.retrieve', $record_class, $rec_id, undef, undef, undef, 1, $deleted_record)->gather(1) ;
         if (@$record) {
             $response = HTTP::OAI::GetRecord->new();
             my $o = "Fieldmapper::oai::$record_class"->new(@$record[0]);
@@ -311,14 +311,14 @@ sub listIdentifiers {
     my ($record_class, $requestURL, $from, $until, $set, $metadataPrefix, $offset ) = @_;
     my $response;
 
-    my $r = $oai->request('open-ils.oai.list.retrieve', $record_class, $offset, $from, $until, $set, $metadataPrefix, $max_count, $deleted_record)->gather(1) ;
+    my $r = $oai->request('open-ils.oai.list.retrieve', $record_class, $offset, $from, $until, $set, $max_count, $deleted_record)->gather(1) ;
     if (@$r) {
         my $cursor = 0 ;
         $response = HTTP::OAI::ListIdentifiers->new();
         for my $record (@$r) {
             my $o = "Fieldmapper::oai::$record_class"->new($record) ;
             if ( $cursor++ == $max_count ) {
-                my $token = new HTTP::OAI::ResumptionToken( resumptionToken => encode_base64(join( '$', $metadataPrefix, $from, $until, $oai_sets->{$set}->{setSpec}, $o->tcn ), '' ) ) ;
+                my $token = new HTTP::OAI::ResumptionToken( resumptionToken => encode_base64(join( '$', $metadataPrefix, $from, $until, $oai_sets->{$set}->{setSpec}, $o->rec_id ), '' ) ) ;
                 $token->cursor($offset);
                 $response->resumptionToken($token) ;
             } else {
@@ -340,14 +340,14 @@ sub listRecords {
     my ($record_class, $requestURL, $from, $until, $set, $metadataPrefix, $offset ) = @_;
     my $response;
 
-    my $r = $oai->request('open-ils.oai.list.retrieve', $record_class, $offset, $from, $until, $set, $metadataPrefix, $max_count, $deleted_record)->gather(1) ;
+    my $r = $oai->request('open-ils.oai.list.retrieve', $record_class, $offset, $from, $until, $set, $max_count, $deleted_record)->gather(1) ;
     if (@$r) {
         my $cursor = 0 ;
         $response = HTTP::OAI::ListRecords->new();
         for my $record (@$r) {
             my $o = "Fieldmapper::oai::$record_class"->new($record) ;
             if ( $cursor++ == $max_count ) {
-                my $token = new HTTP::OAI::ResumptionToken( resumptionToken => encode_base64(join( '$', $metadataPrefix, $from, $until, $oai_sets->{$set}->{setSpec}, $o->tcn ), '' ) ) ;
+                my $token = new HTTP::OAI::ResumptionToken( resumptionToken => encode_base64(join( '$', $metadataPrefix, $from, $until, $oai_sets->{$set}->{setSpec}, $o->rec_id ), '' ) ) ;
                 $token->cursor($offset);
                 $response->resumptionToken($token) ;
             } else {
@@ -378,7 +378,7 @@ sub _header {
     }
 
     return new HTTP::OAI::Header(
-            identifier  => $scheme . $delimiter . $repository_identifier . $delimiter . $o->tcn,
+            identifier  => $scheme . $delimiter . $repository_identifier . $delimiter . $o->rec_id,
             datestamp   => substr($o->datestamp, 0, 19) . 'Z',
             status      => $status,
             setSpec     => \@set_spec
@@ -395,7 +395,7 @@ sub _record {
 
     if ( $o->deleted eq 'f' ) {
         my $md = new HTTP::OAI::Metadata() ;
-        my $xml = $oai->request('open-ils.oai.' . $record_class . '.retrieve', $o->tcn, $metadataPrefix)->gather(1) ;
+        my $xml = $oai->request('open-ils.oai.' . $record_class . '.retrieve', $o->rec_id, $metadataPrefix)->gather(1) ;
         $md->dom( $parser->parse_string('<metadata>' . $xml . '</metadata>') ); # Not sure why I need to add the metadata element,
         $record->metadata( $md );                                               # because I expect ->metadata() would provide the wrapper for it.
     }
diff --git a/Open-ILS/src/sql/Pg/600.schema.oai.sql b/Open-ILS/src/sql/Pg/600.schema.oai.sql
new file mode 100644 (file)
index 0000000..1b5f570
--- /dev/null
@@ -0,0 +1,25 @@
+-- VIEWS for the oai service
+CREATE SCHEMA oai;
+
+-- The view presents a lean table with unique bre.tc-numbers for oai paging;
+CREATE VIEW oai.biblio AS
+  SELECT
+    bre.id                             AS rec_id,
+    bre.edit_date                      AS datestamp,
+    bre.deleted                        AS deleted
+  FROM
+    biblio.record_entry bre
+  ORDER BY
+    bre.id;
+
+-- The view presents a lean table with unique are.tc-numbers for oai paging;
+CREATE VIEW oai.authority AS
+  SELECT
+    are.id               AS rec_id,
+    are.edit_date        AS datestamp,
+    are.deleted          AS deleted
+  FROM
+    authority.record_entry AS are
+  ORDER BY
+    are.id;
+
diff --git a/Open-ILS/src/sql/Pg/oai.sql b/Open-ILS/src/sql/Pg/oai.sql
deleted file mode 100644 (file)
index 64688b9..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
--- VIEWS for the oai service
-CREATE SCHEMA oai;
-
--- The view presents a lean table with unique bre.tc-numbers for oai paging;
-CREATE VIEW oai.biblio AS
-  SELECT
-    bre.id                             AS tcn,
-    bre.edit_date                      AS datestamp,
-    bre.deleted                        AS deleted
-  FROM
-    biblio.record_entry bre
-  ORDER BY
-    bre.id;
-
--- The view presents a lean table with unique are.tc-numbers for oai paging;
-CREATE VIEW oai.authority AS
-  SELECT
-    are.id               AS tcn,
-    are.edit_date        AS datestamp,
-    are.deleted          AS deleted
-  FROM
-    authority.record_entry AS are
-  ORDER BY
-    are.id;
-
--- If an edit date changes in the asset.call_number or asset.copy and you want this to persist to an OAI2 datestamp,
--- then add these stored procedures and triggers:
-CREATE OR REPLACE FUNCTION oai.datestamp(rid BIGINT)
-  RETURNS VOID AS $$
-BEGIN
-  UPDATE biblio.record_entry AS bre
-  SET edit_date = now()
-  WHERE bre.id = rid;
-END
-$$ LANGUAGE plpgsql;
-
-CREATE OR REPLACE FUNCTION oai.call_number_datestamp()
-  RETURNS TRIGGER AS $$
-BEGIN
-  IF TG_OP = 'DELETE'
-  THEN
-    PERFORM oai.datestamp(OLD.record);
-    RETURN OLD;
-  END IF;
-
-  PERFORM oai.datestamp(NEW.record);
-  RETURN NEW;
-
-END
-$$ LANGUAGE plpgsql;
-
-CREATE OR REPLACE FUNCTION oai.copy_datestamp()
-  RETURNS TRIGGER AS $$
-BEGIN
-  IF TG_OP = 'DELETE'
-  THEN
-    PERFORM oai.datestamp((SELECT acn.record FROM asset.call_number as acn WHERE acn.id = OLD.call_number));
-    RETURN OLD;
-  END IF;
-
-  PERFORM oai.datestamp((SELECT acn.record FROM asset.call_number as acn WHERE acn.id = NEW.call_number));
-  RETURN NEW;
-
-END
-$$ LANGUAGE plpgsql;
-
-CREATE TRIGGER call_number_datestamp AFTER INSERT OR UPDATE OR DELETE ON asset.call_number FOR EACH ROW EXECUTE PROCEDURE oai.call_number_datestamp();
-CREATE TRIGGER copy_datestamp AFTER INSERT OR UPDATE OR DELETE ON asset.copy FOR EACH ROW EXECUTE PROCEDURE oai.copy_datestamp();
\ No newline at end of file
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.oai_views.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.oai_views.sql
new file mode 100644 (file)
index 0000000..ef89efc
--- /dev/null
@@ -0,0 +1,79 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+-- VIEWS for the oai service
+CREATE SCHEMA oai;
+
+-- The view presents a lean table with unique bre.tc-numbers for oai paging;
+CREATE VIEW oai.biblio AS
+  SELECT
+    bre.id                             AS rec_id,
+    bre.edit_date                      AS datestamp,
+    bre.deleted                        AS deleted
+  FROM
+    biblio.record_entry bre
+  ORDER BY
+    bre.id;
+
+-- The view presents a lean table with unique are.tc-numbers for oai paging;
+CREATE VIEW oai.authority AS
+  SELECT
+    are.id               AS rec_id,
+    are.edit_date        AS datestamp,
+    are.deleted          AS deleted
+  FROM
+    authority.record_entry AS are
+  ORDER BY
+    are.id;
+
+
+-- OPTIONAL PORTION
+
+\qecho If an edit date changes in the asset.call_number or asset.copy and you want this to persist to an OAI2 datestamp,
+\qecho then add these stored procedures and triggers:
+\qecho
+\qecho 'CREATE OR REPLACE FUNCTION oai.datestamp(rid BIGINT)'
+\qecho '  RETURNS VOID AS $$'
+\qecho 'BEGIN'
+\qecho '  UPDATE biblio.record_entry AS bre'
+\qecho '  SET edit_date = now()'
+\qecho '  WHERE bre.id = rid;'
+\qecho 'END'
+\qecho '$$ LANGUAGE plpgsql;'
+\qecho
+\qecho 'CREATE OR REPLACE FUNCTION oai.call_number_datestamp()'
+\qecho '  RETURNS TRIGGER AS $$'
+\qecho 'BEGIN'
+\qecho '  IF TG_OP = ''DELETE'''
+\qecho '  THEN'
+\qecho '    PERFORM oai.datestamp(OLD.record);'
+\qecho '    RETURN OLD;'
+\qecho '  END IF;'
+\qecho
+\qecho '  PERFORM oai.datestamp(NEW.record);'
+\qecho '  RETURN NEW;'
+\qecho
+\qecho 'END'
+\qecho '$$ LANGUAGE plpgsql;'
+\qecho
+\qecho 'CREATE OR REPLACE FUNCTION oai.copy_datestamp()'
+\qecho '  RETURNS TRIGGER AS $$'
+\qecho 'BEGIN'
+\qecho '  IF TG_OP = ''DELETE'''
+\qecho '  THEN'
+\qecho '    PERFORM oai.datestamp((SELECT acn.record FROM asset.call_number as acn WHERE acn.id = OLD.call_number));'
+\qecho '    RETURN OLD;'
+\qecho '  END IF;'
+\qecho
+\qecho '  PERFORM oai.datestamp((SELECT acn.record FROM asset.call_number as acn WHERE acn.id = NEW.call_number));'
+\qecho '  RETURN NEW;'
+\qecho
+\qecho 'END'
+\qecho '$$ LANGUAGE plpgsql;'
+\qecho
+\qecho 'CREATE TRIGGER call_number_datestamp AFTER INSERT OR UPDATE OR DELETE ON asset.call_number FOR EACH ROW EXECUTE PROCEDURE oai.call_number_datestamp();'
+\qecho 'CREATE TRIGGER copy_datestamp AFTER INSERT OR UPDATE OR DELETE ON asset.copy FOR EACH ROW EXECUTE PROCEDURE oai.copy_datestamp();'
+
+COMMIT;
+
diff --git a/docs/RELEASE_NOTES_NEXT/OAI2/install.adoc b/docs/RELEASE_NOTES_NEXT/OAI2/install.adoc
deleted file mode 100644 (file)
index e54eb3a..0000000
+++ /dev/null
@@ -1,408 +0,0 @@
-= oai-openils is an openSRF service
-
-This module is an optional service that exposes your catalog through the [OAI2 protocol](http://www.openarchives.org/OAI/openarchivesprotocol.html).
-
-== 1. Intended behaviour
-
-=== 1.1 Entry points
-There are two: one for bibliographic records and one for authority records:
-
-    http://your-domain/opac/extras/oai/authority
-    http://your-domain/opac/extras/oai/biblio
-=== 1.2 Setspec are not implemented
-
-This is a work in progress and not enabled. The aim is to have the owning library determine the set hierarchy. The Concerto
-test database for example has a record with tcn #1. This record is so popular it has copies attached to library units
-"Example Branch 1", "Example Branch 2", "Example Branch 3", "Example Bookmobile 1" which is a child of Branch 3 and
-"Example Branch 4". This entire kinship is expressed as sets like so: 
-
-```xml
-<header>
-    ...
-    <setSpec>CONS</setSpec>
-    <setSpec>CONS:SYS1</setSpec>
-    <setSpec>CONS:SYS2</setSpec>
-    <setSpec>CONS:SYS1:BR1</setSpec>
-    <setSpec>CONS:SYS1:BR2</setSpec>
-    <setSpec>CONS:SYS2:BR3</setSpec>
-    <setSpec>CONS:SYS2:BR4</setSpec>
-    <setSpec>CONS:SYS2:BR3:BM1</setSpec>
-</header>
-```
-Likewise the setSpecs of authority records are derived from their browse axis ( Title, Author, Subject and Topic ).
-
-=== 1.3 OAI2 datestamp
-
-The edit date of the bibliographic and authority record is used as datestamp. If you want the date for editorial updates
-of bibliographic assets ( copies, call numbers ) reflected in the datestamp, then add the triggers shown below.
-
-=== 1.4 Bibliographic mapping of assets to 852 subfields
-
-Certain attributes asset are placed into 852 subfields so:
-
-| subfield code | asset resource |
-| --- | --- |
-| a | location |
-| b | owning_lib |
-| c | callnumber |
-| d | circlib |
-| g | barcode |
-| n | status |
-Thus the Concerto with tcn #1 will have it's 852 subfields expressed as:
-```xml
-<marc:datafield ind1="4" ind2=" " tag="852">
-    <marc:subfield code="a">Stacks</marc:subfield>
-    <marc:subfield code="b">BR4</marc:subfield>
-    <marc:subfield code="c">ML 60 R100</marc:subfield>
-    <marc:subfield code="d">BR4</marc:subfield>
-    <marc:subfield code="g">CONC70000435</marc:subfield>
-    <marc:subfield code="n">Checked out</marc:subfield>
-</marc:datafield>
-```
-This mapping can be customized and extended with static subfields:
-```xml
-    <marc:subfield code="q">A constant value</marc:subfield>
-```
-
-=== 1.5 Default configuration
-
-All default configuration is commented in the open-ils.oai app_settings element. See below for details on how to
-override defaults by removing the comments and substitute the values.
-
-== 2. Installation
-
-=== 2.1 Perl modules
-
-Lookup the Perl handler and the associated openils module:
-
- - [Open-ILS/src/perlmods/lib/OpenILS/WWW/OAI.pm](Open-ILS/src/perlmods/lib/OpenILS/WWW/OAI.pm)
- - [Open-ILS/src/perlmods/lib/OpenILS/Application/OAI.pm](Open-ILS/src/perlmods/lib/OpenILS/Application/OAI.pm)
-
-Place them in your codebase next to the other openils modules and let them thus become part of the build:
-
-    Open-ILS/src/perlmods/lib/OpenILS/Application/OAI.pm
-    Open-ILS/src/perlmods/lib/OpenILS/WWW/OAI.pm
-
-or copy the files (owned by the opensrf user) on your servers that host the openils services in the Perl library path:
-
-    /the perl library path/OpenILS/Application/OAI.pm
-    /the perl library path/OpenILS/WWW/OAI.pm
-
-=== 2.2 Declare the perl handler
-
-Declare the Perl handler in the Apache eg_startup file:
-
-```perl
-use OpenILS::WWW::OAI qw( <openils sysdir>conf/opensrf_core.xml );
-```
-    
-And reference it in the Apache eg_vhost.conf file, apache 2.2:
-
-    <Location /opac/extras/oai>
-        SetHandler perl-script
-        PerlHandler OpenILS::WWW::OAI
-        Options +ExecCGI
-        PerlSendHeader On
-        allow from all
-    </Location>
-
-or apache 2.4
-
-    <Location /opac/extras/oai>
-        SetHandler perl-script
-        PerlHandler OpenILS::WWW::OAI
-        Options +ExecCGI
-        PerlSendHeader On
-        Require all granted
-    </Location>
-
-In the eg.conf file under 'PerlRequire /etc/apache2/eg_startup' add:
-```apache
-PerlChildInitHandler OpenILS::WWW::OAI::child_init
-
-```
-
-=== 2.3 The database and fieldmapper
-
-==== 2.3.1 Database
-
-The service requires a view and stored procedures: Open-ILS/src/sql/Pg/oai.sql
-
-Add the oai section to the database:
-```sql
--- VIEWS for the oai service
-CREATE SCHEMA oai;
-
-
--- The view presents a lean table with unique bre.tc-numbers for oai paging;
-CREATE VIEW oai.biblio AS
-  SELECT
-    bre.id                             AS tcn,
-    bre.edit_date                      AS datestamp,
-    bre.deleted                        AS deleted
-  FROM
-    biblio.record_entry bre
-  ORDER BY
-    bre.id;
-
--- The view presents a lean table with unique are.tc-numbers for oai paging;
-CREATE VIEW oai.authority AS
-  SELECT
-    are.id               AS tcn,
-    are.edit_date        AS datestamp,
-    are.deleted          AS deleted
-  FROM
-    authority.record_entry AS are
-  ORDER BY
-    are.id;
-```
-
-==== 2.3.2 Optional, setting the datestamp
-
-If you want the OAI2 datestamp to reflect changes in assets as well, add the following triggers
- ```sql
--- If an edit date changes in the asset.call_number or asset.copy and you want this to persist to an OAI2 datestamp,
--- then add these stored procedures and triggers:
-CREATE OR REPLACE FUNCTION oai.datestamp(rid BIGINT)
-  RETURNS VOID AS $$
-BEGIN
-  UPDATE biblio.record_entry AS bre
-  SET edit_date = now()
-  WHERE bre.id = rid;
-END
-$$ LANGUAGE plpgsql;
-
-CREATE OR REPLACE FUNCTION oai.call_number_datestamp()
-  RETURNS TRIGGER AS $$
-BEGIN
-  IF TG_OP = 'DELETE'
-  THEN
-    PERFORM oai.datestamp(OLD.record);
-    RETURN OLD;
-  END IF;
-
-  PERFORM oai.datestamp(NEW.record);
-  RETURN NEW;
-
-END
-$$ LANGUAGE plpgsql;
-
-CREATE OR REPLACE FUNCTION oai.copy_datestamp()
-  RETURNS TRIGGER AS $$
-BEGIN
-  IF TG_OP = 'DELETE'
-  THEN
-    PERFORM oai.datestamp((SELECT acn.record FROM asset.call_number as acn WHERE acn.id = OLD.call_number));
-    RETURN OLD;
-  END IF;
-
-  PERFORM oai.datestamp((SELECT acn.record FROM asset.call_number as acn WHERE acn.id = NEW.call_number));
-  RETURN NEW;
-
-END
-$$ LANGUAGE plpgsql;
-
-CREATE TRIGGER call_number_datestamp AFTER INSERT OR UPDATE OR DELETE ON asset.call_number FOR EACH ROW EXECUTE PROCEDURE oai.call_number_datestamp();
-CREATE TRIGGER copy_datestamp AFTER INSERT OR UPDATE OR DELETE ON asset.copy FOR EACH ROW EXECUTE PROCEDURE oai.copy_datestamp(); 
- ```
-
-==== 2.3.3 The fieldmapper
-
-Proceed by declaring the views in the fm_IDL.xml file so, as the example shows here [Open-ILS/examples/fm_ILD.xml](Open-ILS/examples/fm_IDL.xml):
-
-```xml
-<class id="oai_biblio" controller="open-ils.cstore" oils_obj:fieldmapper="oai::biblio"
-       oils_persist:readonly="true" reporter:core="false" reporter:label="OAI2 record list"
-       oils_persist:tablename="oai.biblio">
-    <fields>
-        <field reporter:label="TCN Value\OAI identifier postfix" name="tcn" reporter:datatype="number"/>
-        <field reporter:label="Last edit date\OAI datestamp" name="datestamp" reporter:datatype="timestamp"/>
-        <field reporter:label="Is Deleted?" name="deleted" reporter:datatype="bool"/>
-        <field reporter:label="Setspec" name="set_spec" oils_persist:virtual="true"/>
-    </fields>
-</class>
-<class id="oai_authority" controller="open-ils.cstore" oils_obj:fieldmapper="oai::authority"
-       oils_persist:readonly="true" reporter:core="false" reporter:label="OAI2 record list"
-       oils_persist:tablename="oai.authority">
-    <fields>
-        <field reporter:label="TCN Value\OAI identifier postfix" name="tcn" reporter:datatype="number"/>
-        <field reporter:label="Last edit date\OAI datestamp" name="datestamp" reporter:datatype="timestamp"/>
-        <field reporter:label="Is Deleted?" name="deleted" reporter:datatype="bool"/>
-        <field reporter:label="Setspec" name="set_spec" oils_persist:virtual="true"/>
-    </fields>
-</class>
-```
-
-=== 2.4 The xslt stylesheets
-
-Lookup the two documents here:
-
- - [Open-ILS/xsl/OAI2_OAIDC.xsl](Open-ILS/xsl/OAI2_OAIDC.xsl)
- - [Open-ILS/xsl/OAI2_MARC21slim.xsl](Open-ILS/xsl/OAI2_MARC21slim.xsl)
-
-Place the stylesheets in your codebase next to the other xsl documents and let them thus become part of the build.
-Or install them on your servers that host the openils services:
-
-    /<openils sysdir>/var/xsl/OAI2_OAIDC.xsl
-    /<openils sysdir>/var/xsl/OAI2_MARC21slim.xsl
-    
-=== 2.5 Dependencies
-The openils-oai service depends on a running openils-supercat service.
-And the OAI2_OAIDC.xsl document uses the file [MARC21slim2OAIDC.xsl](Open-ILS/xsl/MARC21slim2OAIDC.xsl).
-The service and stylesheet are part of the out-of-the-box Evergreen distributions.
-        
-But do install the ['HTTP::OAI' perl library from a CPAN repository](http://search.cpan.org/dist/HTTP-OAI/):
-
-    $ cpan HTTP::OAI    
-    
-
-== 3. Configuration
-
-=== 3.1 Declare the service
-
-Add the openils-oai service to your /&lt;openils sysdir&gt;/conf/opensrf.xml file.
-```xml
-....
-<open-ils.oai>
-    <keepalive>5</keepalive>
-    <stateless>1</stateless>
-    <language>perl</language>
-    <implementation>OpenILS::Application::OAI</implementation>
-    <max_requests>199</max_requests>
-    <unix_config>
-        <unix_sock>open-ils.oai_unix.sock</unix_sock>
-        <unix_pid>open-ils.oai_unix.pid</unix_pid>
-        <max_requests>1000</max_requests>
-        <unix_log>open-ils.oai_unix.log</unix_log>
-        <min_children>1</min_children>
-        <max_children>5</max_children>
-        <min_spare_children>1</min_spare_children>
-        <max_spare_children>2</max_spare_children>
-    </unix_config>
-    <app_settings>
-
-        <!-- Where necessary, override the default settings here in the app_settings element. -->
-
-        <!-- The OAI endpoint. The domain is the name of your proxy or frontend opac website. -->
-        <!-- <base_url>http://mydomain.org/opac/extras/oai</base_url> -->
-
-        <!-- <repository_name>My organization(s)</repository_name> -->
-        <!-- <admin_email>admin@mydomain.org</admin_email> -->
-
-        <!-- The maximum number of records in a ListRecords and ListIdentifiers response. -->
-        <!-- <max_count>50</max_count> -->
-
-        <!-- <granularity>YYYY-MM-DDThh:mm:ss</granularity> -->
-        <!-- <earliest_datestamp>0001-01-01</earliest_datestamp> -->
-        <!-- <deleted_record>yes</deleted_record> -->
-        <!-- <scheme>oai</scheme> -->
-        <!-- <repository_identifier>mydomain.org</repository_identifier> -->
-        <!-- <delimiter>:</delimiter> -->
-        <!-- <sample_identifier>oai:mydomain.org:12345</sample_identifier> -->
-        <!-- <list_sets>false</list_sets> -->
-
-        <!--
-        The metadataformat element contains the schema for the oai_dc and marcxml metadata formats.
-        Each schema needs a reference to an xslt document.
-        You can replace them with your custom xslt stylesheets.
-        Place those in the /<openils sysdir>/var/xsl folder.
-        You can also extend the OAI2 service further with new metadata schema.
-        
-        Bibliographic and authority records share the same stylesheet.
-        Should you want to render them differently, use the
-        marc:datafield[@tag='901']/marc:subfield[@code='t']
-        value to identify the record type. -->
-
-        <!--
-        <metadataformat>
-            <oai_dc>
-                <namespace_uri>http://www.openarchives.org/OAI/2.0/oai_dc/</namespace_uri>
-                <schema_location>http://www.openarchives.org/OAI/2.0/oai_dc.xsd</schema_location>
-                <xslt>OAI2_OAIDC.xsl</xslt>
-            </oai_dc>
-            <marcxml>
-                <namespace_uri>http://www.loc.gov/MARC21/slim</namespace_uri>
-                <schema_location>http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd</schema_location>
-                <xslt>OAI2_MARC21slim.xsl</xslt>
-            </marcxml>
-        </metadataformat> -->
-
-        <!--
-        You can add different schema to the metadataformat element thus:
-            <mods>
-                <namespace_uri>http://www.loc.gov/mods/</namespace_uri>
-                <schema_location>http://www.loc.gov/standards/mods/mods.xsd</schema_location>
-                <xslt>my-custom-marc2mods.xsl</xslt>
-            </mods>
-            <my-metadata_prefix>
-                <namespace_uri>my-namespace_uri</namespace_uri>
-                <schema_location>my-schema_location</schema_location>
-                <xslt>my-marc2my-metadata.xsl</xslt>
-            </my-metadata_prefix>
-        -->
-
-        <!-- Change the way the asset copy values are mapped to which subfield codes: -->
-        <!--
-        <copies>
-            <a>location</a>
-            <b>owning_lib</b>
-            <c>callnumber</c>
-            <d>circlib</d>
-            <g>barcode</g>
-            <n>status</n>
-        </copies>
-        -->
-        <!-- Or add static values to the copies element like this:
-            <z>A value that always should for example be in the 852$z</z>
-        -->
-        
-        <!-- Accept only 852$[barcode] values that match this regular expression. E.g.
-        <barcode_filter>^[A-Za-z0-9]+</barcode_filter>
-        only renders 852 datafields that contain barcodes values that begin with letters and numbers. 
-        <!--
-        <barcode_filter><barcode_filter>
-        -->
-                
-        <!-- Accept only 852$[status] values that match this regular expression. E.g.
-        <status_filter>^Available$</status_filter>
-        only renders 852 datafields that contain status code values that exactly match the string 'Available'. 
-        <!--
-        <status_filter></status_filter>
-        -->
-
-    </app_settings>
-</open-ils.oai>
-```
-
-==== 3.2 Activate the service
-
-Refer to the service in the opensrf.xml's activeapps element:
-```xml
-....
-<activeapps>
-    <appname>open-ils.oai</appname>
-```
-
-==== 3.3 Register the service with the router
-
-Add the service to the public router with your /&lt;openils sysdir&gt;/conf/opensrf_core.xml
-```xml
-<config>
-    <opensrf>
-        <routers>
-            <router>
-                <name>router</name>
-                <domain>public.realm</domain>
-                <services>
-                    <service>openils.oai</service>
-                    ...
-```
-    
-
-
-
-
-
diff --git a/docs/RELEASE_NOTES_NEXT/OAI2/new_oai_opensrf_service.adoc b/docs/RELEASE_NOTES_NEXT/OAI2/new_oai_opensrf_service.adoc
new file mode 100644 (file)
index 0000000..495ba5f
--- /dev/null
@@ -0,0 +1,160 @@
+New OAI Service
+^^^^^^^^^^^^^^^
+
+This module is an optional service that exposes your catalog through the [OAI2 protocol](http://www.openarchives.org/OAI/openarchivesprotocol.html).
+
+
+Entry points
+++++++++++++
+There are two: one for bibliographic records and one for authority records:
+
+    http://your-domain/opac/extras/oai/authority
+    http://your-domain/opac/extras/oai/biblio
+
+An example of a working URL on a system with an authority record with ID
+1:
+
+    http://your-domain/opac/extras/oai/authority?verb=GetRecord&identifier=oai:localhost:1&metadataPrefix=oai_dc
+Setspec are not implemented
++++++++++++++++++++++++++++
+
+This is a work in progress and not enabled. The aim is to have the owning library determine the set hierarchy. The Concerto
+test database for example has a record with record ID #1. This record is so popular it has copies attached to library units
+"Example Branch 1", "Example Branch 2", "Example Branch 3", "Example Bookmobile 1" which is a child of Branch 3 and
+"Example Branch 4". This entire kinship is expressed as sets like so: 
+
+```xml
+<header>
+    ...
+    <setSpec>CONS</setSpec>
+    <setSpec>CONS:SYS1</setSpec>
+    <setSpec>CONS:SYS2</setSpec>
+    <setSpec>CONS:SYS1:BR1</setSpec>
+    <setSpec>CONS:SYS1:BR2</setSpec>
+    <setSpec>CONS:SYS2:BR3</setSpec>
+    <setSpec>CONS:SYS2:BR4</setSpec>
+    <setSpec>CONS:SYS2:BR3:BM1</setSpec>
+</header>
+```
+Likewise the setSpecs of authority records are derived from their browse axis ( Title, Author, Subject and Topic ).
+
+Bibliographic mapping of assets to 852 subfields
+++++++++++++++++++++++++++++++++++++++++++++++++
+
+Certain attributes asset are placed into 852 subfields so:
+
+|===
+| subfield code | asset resource
+
+| a | location
+| b | owning_lib
+| c | callnumber
+| d | circlib
+| g | barcode
+| n | status
+|===
+Thus the Concerto with record ID #1 will have it's 852 subfields expressed as:
+```xml
+<marc:datafield ind1="4" ind2=" " tag="852">
+    <marc:subfield code="a">Stacks</marc:subfield>
+    <marc:subfield code="b">BR4</marc:subfield>
+    <marc:subfield code="c">ML 60 R100</marc:subfield>
+    <marc:subfield code="d">BR4</marc:subfield>
+    <marc:subfield code="g">CONC70000435</marc:subfield>
+    <marc:subfield code="n">Checked out</marc:subfield>
+</marc:datafield>
+```
+This mapping can be customized and extended with static subfields:
+```xml
+    <marc:subfield code="q">A constant value</marc:subfield>
+```
+
+Default configuration
++++++++++++++++++++++
+
+See comments in opensrf.xml (in the open-ils.oai app_settings element)
+for default configuration and customization instructions. is commented
+in the open-ils.oai app_settings element.
+
+Upgrade Instructions
+++++++++++++++++++++
+
+**Activate the service**
+
+Refer to the service in the opensrf.xml activeapps element:
+```xml
+....
+<activeapps>
+    <appname>open-ils.oai</appname>
+```
+
+**Register the service with the router**
+
+Add the service to the public router in your opensrf_core.xml
+```xml
+<config>
+    <opensrf>
+        <routers>
+            <router>
+                <name>router</name>
+                <domain>public.realm</domain>
+                <services>
+                    <service>openils.oai</service>
+                    ...
+```
+
+Optional: Setting the datestamp
++++++++++++++++++++++++++++++++
+
+The edit date of the bibliographic and authority record is used as
+datestamp. If you want the date for editorial updates of bibliographic
+assets (i.e. copies, call numbers) reflected in the datestamp, then add the
+triggers shown below.
+
+```sql
+CREATE OR REPLACE FUNCTION oai.datestamp(rid BIGINT)
+  RETURNS VOID AS $$
+BEGIN
+  UPDATE biblio.record_entry AS bre
+  SET edit_date = now()
+  WHERE bre.id = rid;
+END
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION oai.call_number_datestamp()
+  RETURNS TRIGGER AS $$
+BEGIN
+  IF TG_OP = 'DELETE'
+  THEN
+    PERFORM oai.datestamp(OLD.record);
+    RETURN OLD;
+  END IF;
+
+  PERFORM oai.datestamp(NEW.record);
+  RETURN NEW;
+
+END
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION oai.copy_datestamp()
+  RETURNS TRIGGER AS $$
+BEGIN
+  IF TG_OP = 'DELETE'
+  THEN
+    PERFORM oai.datestamp((SELECT acn.record FROM asset.call_number as acn WHERE acn.id = OLD.call_number));
+    RETURN OLD;
+  END IF;
+
+  PERFORM oai.datestamp((SELECT acn.record FROM asset.call_number as acn WHERE acn.id = NEW.call_number));
+  RETURN NEW;
+
+END
+$$ LANGUAGE plpgsql;
+
+CREATE TRIGGER call_number_datestamp AFTER INSERT OR UPDATE OR DELETE ON asset.call_number FOR EACH ROW EXECUTE PROCEDURE oai.call_number_datestamp();
+CREATE TRIGGER copy_datestamp AFTER INSERT OR UPDATE OR DELETE ON asset.copy FOR EACH ROW EXECUTE PROCEDURE oai.copy_datestamp(); 
+```
+