LP1819179 PCRUD selector fleshing handles maps
authorBill Erickson <berickxx@gmail.com>
Fri, 8 Mar 2019 18:16:35 +0000 (13:16 -0500)
committerDan Wells <dbw2@calvin.edu>
Fri, 22 Mar 2019 20:23:37 +0000 (16:23 -0400)
Teach the PCUD selector fleshing code to handle selector fields on
mapped classes, where an intermediate object flesh is performed by pcrud
at flesh time.

Adds a 'selector' column on metabib.metarecord in the IDL so that we can
have a functioning example of this to use in the sandbox test code.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Dan Wells <dbw2@calvin.edu>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/eg2/src/app/core/format.service.ts
Open-ILS/src/eg2/src/app/core/idl.service.ts
Open-ILS/src/eg2/src/app/core/pcrud.service.ts
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts

index db65187..1f51073 100644 (file)
@@ -3934,7 +3934,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
        <class id="mmr" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="metabib::metarecord" oils_persist:tablename="metabib.metarecord" reporter:label="Metarecord">
                <fields oils_persist:primary="id" oils_persist:sequence="metabib.metarecord_id_seq">
                        <field name="fingerprint"  reporter:datatype="text"/>
-                       <field name="id" reporter:datatype="id" />
+                       <field name="id" reporter:datatype="id" reporter:selector="fingerprint" />
                        <field name="master_record" reporter:datatype="link"/>
                        <field name="mods"  reporter:datatype="text"/>
                        <field name="source_records" oils_persist:virtual="true" reporter:datatype="link"/>
index ad7e9ce..e788cd0 100644 (file)
@@ -78,37 +78,29 @@ export class FormatService {
 
                 if (!params.idlClass || !params.idlField) {
                     // Without a full accounting of the field data,
-                    // we can't determine the display value.
+                    // we can't determine the linked selector field.
                     return value + '';
                 }
 
-                const localClass = this.idl.classes[params.idlClass];
+                const selector =
+                    this.idl.getLinkSelector(params.idlClass, params.idlField);
 
-                if (!localClass) {
-                    console.warn(`No such IDL class ${params.idlClass}`);
-                    return value + '';
-                }
+                if (selector && typeof value[selector] === 'function') {
+                    const val = value[selector]();
 
-                if (!localClass.field_map[params.idlField]) {
-                    console.warn(`IDL class ${params.idlClass} ` +
-                        `has no field named "${params.idlField}"`);
-                    return value + '';
-                }
+                    if (Array.isArray(val)) {
+                        // Typically has_many links will not be fleshed,
+                        // but in the off-chance the are, avoid displaying
+                        // an array reference value.
+                        return '';
+                    } else {
+                        return val + '';
+                    }
 
-                const linkType = localClass.field_map[params.idlField]['reltype'];
-                if (linkType !== 'has_a') {
-                    return value + ''; // eh?
+                } else {
+                    return value + '';
                 }
 
-                const localField = localClass.field_map[params.idlField];
-                const remoteKey = localField['key'];
-
-                const remoteClass = this.idl.classes[localField['class']];
-                const remoteField = remoteClass.field_map[remoteKey];
-                const remoteSelector = remoteField.selector || remoteField.name;
-
-                return value[remoteSelector]() + '';
-
             case 'org_unit':
                 const orgField = params.orgField || 'shortname';
                 const org = this.org.get(value);
index b6f8173..56b8b90 100644 (file)
@@ -138,7 +138,16 @@ export class IdlService {
     // on the linked class that acts as the selector for the linked class.
     // Returns null if no selector is found or the field is not a link.
     getLinkSelector(fmClass: string, field: string): string {
-        const fieldDef = this.classes[fmClass].field_map[field];
+        let fieldDef = this.classes[fmClass].field_map[field];
+
+        if (fieldDef.map) {
+            // For mapped fields, we want the selector field on the
+            // remotely linked object instead of the directly
+            // linked object.
+            const linkedClass = this.classes[fieldDef.class];
+            fieldDef = linkedClass.field_map[fieldDef.map];
+        }
+
         if (fieldDef.class) {
             const classDef = this.classes[fieldDef.class];
             if (classDef.pkey) {
index b3d4288..9e14191 100644 (file)
@@ -104,14 +104,31 @@ export class PcrudContext {
         }
 
         this.idl.classes[fmClass].fields
-        .filter(f => 
+        .filter(f =>
             f.datatype === 'link' && (
-                f.reltype === 'has_a' || f.reltype === 'might_have'    
+                f.reltype === 'has_a' || f.reltype === 'might_have'
             )
         ).forEach(field => {
+
             const selector = this.idl.getLinkSelector(fmClass, field.name);
             if (!selector) { return; }
 
+            if (field.map) {
+                // For mapped fields, we only want to auto-flesh them
+                // if both steps along the path are single-row fleshers.
+
+                const mapClass = field['class'];
+                const mapField = field.map;
+                const def = this.idl.classes[mapClass].field_map[mapField];
+
+                if (!(def.reltype === 'has_a' ||
+                      def.reltype === 'might_have')) {
+                    // Field maps to a remote field which may contain
+                    // multiple rows.  Skip it.
+                    return;
+                }
+            }
+
             if (!pcrudOps.flesh_fields[fmClass]) {
                 pcrudOps.flesh_fields[fmClass] = [];
             }
index 00c2ee7..5f1f1ad 100644 (file)
 
 <br/><br/>
 
+<h4>PCRUD auto flesh and FormatService detection</h4>
+<div *ngIf="aMetarecord">Fingerprint: {{aMetarecord}}</div>
 
index ed2c496..4ee4ebc 100644 (file)
@@ -13,6 +13,7 @@ import {Pager} from '@eg/share/util/pager';
 import {DateSelectComponent} from '@eg/share/date-select/date-select.component';
 import {PrintService} from '@eg/share/print/print.service';
 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+import {FormatService} from '@eg/core/format.service';
 
 @Component({
   templateUrl: 'sandbox.component.html'
@@ -58,12 +59,16 @@ export class SandboxComponent implements OnInit {
     complimentEvergreen: (rows: IdlObject[]) => void;
     notOneSelectedRow: (rows: IdlObject[]) => boolean;
 
+    // selector field value on metarecord object
+    aMetarecord: string;
+
     constructor(
         private idl: IdlService,
         private org: OrgService,
         private pcrud: PcrudService,
         private strings: StringService,
         private toast: ToastService,
+        private format: FormatService,
         private printer: PrintService
     ) {
     }
@@ -114,6 +119,17 @@ export class SandboxComponent implements OnInit {
 
         this.complimentEvergreen = (rows: IdlObject[]) => alert('Evergreen is great!');
         this.notOneSelectedRow = (rows: IdlObject[]) => (rows.length !== 1);
+
+        this.pcrud.retrieve('bre', 1, {}, {fleshSelectors: true})
+        .subscribe(bib => {
+            // Format service will automatically find the selector
+            // value to display from our fleshed metarecord field.
+            this.aMetarecord = this.format.transform({
+                value: bib.metarecord(),
+                idlClass: 'bre',
+                idlField: 'metarecord'
+            });
+        });
     }
 
     btGridRowClassCallback(row: any): string {