diff --git a/__tests__/Utilities.test.js b/__tests__/Utilities.test.js index 8d6b0c641..461836f48 100644 --- a/__tests__/Utilities.test.js +++ b/__tests__/Utilities.test.js @@ -1,12 +1,16 @@ // Copyright 2019 Stanford University see LICENSE for license -import { isResourceWithValueTemplateRef, resourceToName, templateBoolean } from '../src/Utilities' +import { + isResourceWithValueTemplateRef, + resourceToName, + templateBoolean, + defaultValuesFromPropertyTemplate +} from '../src/Utilities' -describe('Utilities', () => { +describe('Utilities', () => { describe('isResourceWithValueTemplateRef()', () => { - - it('true when there is a valueTemplateRef', () => { + it('returns true when there is a valueTemplateRef', () => { const templateWithValueTemplateRefs = { "propertyURI": "http://id.loc.gov/ontologies/bibframe/note", "type": "resource", @@ -19,7 +23,7 @@ describe('Utilities', () => { expect(isResourceWithValueTemplateRef(templateWithValueTemplateRefs)).toBeTruthy() }) - it('true when there are multiple valueTemplateRefs' , () => { + it('returns true when there are multiple valueTemplateRefs' , () => { const templateWithTwoValueTemplateRefs = { "propertyURI": "http://id.loc.gov/ontologies/bibframe/note", "type": "resource", @@ -33,7 +37,7 @@ describe('Utilities', () => { expect(isResourceWithValueTemplateRef(templateWithTwoValueTemplateRefs)).toBeTruthy() }) - it('false when valueTemplateRefs is empty', () => { + it('returns false when valueTemplateRefs is empty', () => { const emptyValueTemplateRefs = { "propertyURI": "http://id.loc.gov/ontologies/bibframe/issuance", "type": "resource", @@ -44,7 +48,7 @@ describe('Utilities', () => { expect(isResourceWithValueTemplateRef(emptyValueTemplateRefs)).toBeFalsy() }) - it('false when there is no valueConstraint', () => { + it('returns false when there is no valueConstraint', () => { const noValueConstraint = { "propertyURI": "http://id.loc.gov/ontologies/bibframe/issuance", "type": "resource" @@ -52,7 +56,7 @@ describe('Utilities', () => { expect(isResourceWithValueTemplateRef(noValueConstraint)).toBeFalsy() }) - it('false when valueConstraint is empty (there are no valueTemplateRefs)', () => { + it('returns false when valueConstraint is empty (there are no valueTemplateRefs)', () => { const emptyValueConstraint = { "propertyURI": "http://id.loc.gov/ontologies/bibframe/issuance", "type": "resource", @@ -61,7 +65,7 @@ describe('Utilities', () => { expect(isResourceWithValueTemplateRef(emptyValueConstraint)).toBeFalsy() }) - it('false when the type is other than resource', () => { + it('returns false when the type is other than resource', () => { const notTypeResource = { "propertyURI": "http://id.loc.gov/ontologies/bibframe/title", "type": "literal", @@ -74,7 +78,7 @@ describe('Utilities', () => { expect(isResourceWithValueTemplateRef(notTypeResource)).toBeFalsy() }) - it('false when there is no type at all', () => { + it('returns false when there is no type at all', () => { const noTypeAtAll = { "propertyURI": "http://id.loc.gov/ontologies/bibframe/note", "valueConstraint": { @@ -89,7 +93,6 @@ describe('Utilities', () => { }) describe('resourceToName()', () => { - it('returns resource name from last path part of URI', () => { const uri = "https://trellis.sinopia.io/resources/ld4p/resourceTemplate:bf2:Note" expect(resourceToName(uri)).toEqual("resourceTemplate:bf2:Note") @@ -107,31 +110,63 @@ describe('Utilities', () => { it('returns undefined when there is no URI', () => { expect(resourceToName()).toEqual(undefined) }) - }) - describe('templateBoolean()', () => { + describe('templateBoolean()', () => { + it('returns true when "true" is passed in as a string', () => { + expect(templateBoolean("true")).toBe(true) + }) - it('returns true when "true" is passed in as a string', () => { - expect(templateBoolean("true")).toBe(true) - }) + it('returns true when a boolean true is passed into the function', () => { + expect(templateBoolean(true)).toBe(true) + }) - it('returns true when a boolean true is passed into the function', () => { - expect(templateBoolean(true)).toBe(true) - }) + it('returns true as the default if the parameter is null, undefined, or a random string', () => { + expect(templateBoolean(null)).toBe(true) + expect(templateBoolean(undefined)).toBe(true) + expect(templateBoolean("asdfdsafds")).toBe(true) + }) - it('returns true as the default if the parameter is null, undefined, or a random string', () => { - expect(templateBoolean(null)).toBe(true) - expect(templateBoolean(undefined)).toBe(true) - expect(templateBoolean("asdfdsafds")).toBe(true) - }) + it('return false when "false" is passed in as a string', () => { + expect(templateBoolean("false")).toBe(false) + }) - it('return false when "false" is passed in as a string', () => { - expect(templateBoolean("false")).toBe(false) + it('returns false when a boolean false is passed into the function', () => { + expect(templateBoolean(false)).toBe(false) + }) }) - it('returns false when a boolean false is passed into the function', () => { - expect(templateBoolean(false)).toBe(false) + describe('defaultValuesFromPropertyTemplate()', () => { + it('returns an empty array if passed any value that fails to define a `valueConstraint.defaults` array', () => { + const propertyTemplate = null + expect(defaultValuesFromPropertyTemplate(propertyTemplate)).toEqual([]) + }) + + it('returns an empty array if passed a value with an empty `valueConstraint.defaults` array', () => { + const propertyTemplate = { + valueConstraint: { + defaults: [] + } + } + expect(defaultValuesFromPropertyTemplate(propertyTemplate)).toEqual([]) + }) + + it('returns an array with an object otherwise', () => { + const propertyTemplate = { + valueConstraint: { + defaults: [ + { + defaultURI: 'http://id.loc.gov/vocabulary/mcolor/mul', + defaultLiteral: 'color' + } + ] + } + } + expect(defaultValuesFromPropertyTemplate(propertyTemplate)).toEqual([{ + id: 'http://id.loc.gov/vocabulary/mcolor/mul', + label: 'color', + uri: 'http://id.loc.gov/vocabulary/mcolor/mul' + }]) + }) }) - }) }) diff --git a/__tests__/components/editor/InputListLOC.test.js b/__tests__/components/editor/InputListLOC.test.js index 7341b0b26..74c24acc8 100644 --- a/__tests__/components/editor/InputListLOC.test.js +++ b/__tests__/components/editor/InputListLOC.test.js @@ -68,13 +68,47 @@ describe('', () => { expect(wrapper.find('#targetComponent').props().placeholder).toMatch('Frequency (RDA 2.14)') }) - it('sets the default values according to the property template if they exist', () => { - const defaults = [{ - id: 'http://id.loc.gov/vocabulary/carriers/nc', - uri: 'http://id.loc.gov/vocabulary/carriers/nc', - label: 'volume' - }] - expect(wrapper.state('defaults')).toEqual(defaults) + describe('default values', () => { + afterAll(() => { + jest.restoreAllMocks() + }) + + it('sets the default values according to the property template if they exist', () => { + const defaults = [{ + id: 'http://id.loc.gov/vocabulary/carriers/nc', + uri: 'http://id.loc.gov/vocabulary/carriers/nc', + label: 'volume' + }] + expect(wrapper.state('defaults')).toEqual(defaults) + }) + + it('logs an error when no defaults are set', () => { + const plProps = { + "propertyTemplate": { + "propertyURI": "http://id.loc.gov/ontologies/bflc/target", + "propertyLabel": "Frequency (RDA 2.14)", + "remark": "http://access.rdatoolkit.org/2.14.html", + "mandatory": "false", + "repeatable": "false", + "type": "lookup", + "valueConstraint": { + "repeatable": "true", + "valueTemplateRefs": [], + "useValuesFrom": [ + "vocabulary:bf2:frequencies" + ], + "valueDataType": { + "dataTypeURI": "http://id.loc.gov/ontologies/bibframe/Frequency" + } + } + } + } + const errorSpy = jest.spyOn(console, 'error').mockReturnValue(null) + const wrapper2 = shallow() + + expect(wrapper2.state('defaults')).toEqual([]) + expect(errorSpy).toBeCalledWith(`no defaults defined in property template: ${JSON.stringify(plProps.propertyTemplate)}`) + }) }) it('should call the onFocus event and set the selected option', () => { diff --git a/__tests__/components/editor/InputLookupQA.test.js b/__tests__/components/editor/InputLookupQA.test.js index e2981df7b..371beba3e 100644 --- a/__tests__/components/editor/InputLookupQA.test.js +++ b/__tests__/components/editor/InputLookupQA.test.js @@ -1,8 +1,9 @@ // Copyright 2018 Stanford University see LICENSE for license + import 'jsdom-global/register' import React from 'react' import { shallow } from 'enzyme' -import InputLookup from '../../../src/components/editor/InputLookupQA' +import InputLookupQA from '../../../src/components/editor/InputLookupQA' const plProps = { "id": "lookupComponent", @@ -20,12 +21,16 @@ const plProps = { "valueDataType": { "dataTypeURI": "http://id.loc.gov/ontologies/bibframe/Agent" }, - "defaults": [] + "defaults": [{ + "defaultURI": "http://id.loc.gov/vocabulary/carriers/nc", + "defaultLiteral": "volume" + }] }, "propertyURI": "http://id.loc.gov/ontologies/bflc/target", "propertyLabel": "Name Lookup" } -}; +} + const p2Props = { "id": "lookupComponent", "propertyTemplate": @@ -48,7 +53,7 @@ const p2Props = { "propertyURI": "http://id.loc.gov/ontologies/bflc/target", "propertyLabel": "Name Lookup" } -}; +} const multipleResults = [{ "authLabel":"Person", @@ -59,12 +64,12 @@ const multipleResults = [{ "authLabel":"Subject", "authURI":"SubjectURI", "body":[{ "uri":"suri","label":"slabel" }] - }]; + }] describe('', () => { // our mock formData function to replace the one provided by mapDispatchToProps const mockFormDataFn = jest.fn() - const wrapper = shallow() + const wrapper = shallow() it('uses the propertyLabel from the template as the form control label', () => { expect(wrapper.find('#lookupComponent').props().placeholder).toMatch('Name Lookup') @@ -83,6 +88,61 @@ describe('', () => { expect(wrapper.find('#lookupComponent').props().multiple).toBeTruthy() }) + describe('default values', () => { + afterAll(() => { + jest.restoreAllMocks() + }) + + it('sets the default values according to the property template if they exist', () => { + const defaults = [{ + id: 'http://id.loc.gov/vocabulary/carriers/nc', + uri: 'http://id.loc.gov/vocabulary/carriers/nc', + label: 'volume' + }] + expect(wrapper.state('defaults')).toEqual(defaults) + }) + + it('logs an error when no defaults are set', () => { + const plProps = { + "id": "lookupComponent", + "propertyTemplate": + { + "mandatory": "false", + "repeatable": "true", + "type": "lookup", + "resourceTemplates": [], + "valueConstraint": { + "valueTemplateRefs": [], + "useValuesFrom": [ + 'lookupQaLocNames' + ], + "valueDataType": { + "dataTypeURI": "http://id.loc.gov/ontologies/bibframe/Agent" + } + }, + "propertyURI": "http://id.loc.gov/ontologies/bflc/target", + "propertyLabel": "Name Lookup" + } + } + + const errorSpy = jest.spyOn(console, 'error').mockReturnValue(null) + const wrapper2 = shallow() + + expect(wrapper2.state('defaults')).toEqual([]) + expect(errorSpy).toBeCalledWith(`no defaults defined in property template: ${JSON.stringify(plProps.propertyTemplate)}`) + }) + + it('sets the async typeahead component defaultSelected attribute', () => { + const wrapper2 = shallow() + + expect(wrapper2.find('#lookupComponent').props().defaultSelected).toEqual([{ + id: 'http://id.loc.gov/vocabulary/carriers/nc', + uri: 'http://id.loc.gov/vocabulary/carriers/nc', + label: 'volume' + }]) + }) + }) + it('should call the onChange event and set the state with the selected option', () => { const event = (wrap) => { wrap.setState({options: ["{uri: 'URI', label: 'LABEL'}"]}) @@ -114,7 +174,7 @@ describe('', () => { }) //Institute wrapper with multiple lookup options - const multipleWrapper = shallow() + const multipleWrapper = shallow() it('passes multiple lookup results in state with search event', () => { const event = (wrap) => { wrap.setState({options: multipleResults}) diff --git a/__tests__/components/editor/PropertyComponent.test.js b/__tests__/components/editor/PropertyComponent.test.js index 4c806505c..79f8db1bc 100644 --- a/__tests__/components/editor/PropertyComponent.test.js +++ b/__tests__/components/editor/PropertyComponent.test.js @@ -5,9 +5,7 @@ import { shallow } from 'enzyme' import PropertyComponent from "../../../src/components/editor/PropertyComponent" describe('', () => { - describe('sets the configuration state based on values from the property template', () => { - describe('for property templates configured as list', () => { const template = { "propertyURI": "http://id.loc.gov/ontologies/bibframe/issuance", @@ -62,31 +60,28 @@ describe('', () => { it('renders the lookup component', () => { expect(wrapper.find('Connect(InputLookupQA)').length).toEqual(1) }) - }) }) describe('when there are no configuration values from the property template', () => { - describe('for unconfigured templates of type:literal', () => { const template = { - "propertyURI": "http://id.loc.gov/ontologies/bibframe/heldBy", + "propertyURI": "http//:id.loc.gov/ontologies/bibframe/heldBy", "type": "literal" } const wrapper = shallow() - it('will return an empty array', () => { + it('returns an empty array', () => { expect(wrapper.state('configuration').length).toEqual(0) }) it('renders an InputLiteral component', () => { expect(wrapper.find('Connect(InputLiteral)').length).toEqual(1) }) - }) - it('if the property type is not literal (i.e. resource) an empty div is returned', () => { + it('returns an empty div if the property type is not literal (i.e. resource)', () => { const template = { "propertyURI": "http://id.loc.gov/ontologies/bibframe/note", "type": "resource", @@ -105,4 +100,53 @@ describe('', () => { }) }) + describe('getLookupConfigItems()', () => { + it('returns an empty array if passed any value that fails to define a `valueConstraint.useValuesFrom` array', () => { + const template = {} + const wrapper = shallow() + + expect(wrapper.instance().getLookupConfigItems(template)).toEqual([]) + }) + + it('returns an empty array if passed a value with an empty `valueConstraint.useValuesFrom` array', () => { + const template = { + valueConstraint: { + useValuseFrom: [] + } + } + const wrapper = shallow() + + expect(wrapper.instance().getLookupConfigItems(template)).toEqual([]) + }) + + it('returns an array with lookupConfig objects matching URIs in useValuesFrom array', () => { + const template = { + valueConstraint: { + useValuesFrom: [ + 'http://does.not.match/1', + 'http://does.not.match/2', + 'urn:ld4p:qa:agrovoc', + 'https://id.loc.gov/vocabulary/mrectype' + ] + } + } + const wrapper = shallow() + + expect(wrapper.instance().getLookupConfigItems(template)).toEqual([ + { + "label": "AGROVOC (QA)", + "uri": "urn:ld4p:qa:agrovoc", + "authority": "agrovoc_ld4l_cache", + "subauthority": "", + "language": "en", + "component": "lookup" + }, + { + "label": "type of recording", + "uri": "https://id.loc.gov/vocabulary/mrectype", + "component": "list" + } + ]) + }) + }) }) diff --git a/src/Utilities.js b/src/Utilities.js index 58a9e4e51..119e47f86 100644 --- a/src/Utilities.js +++ b/src/Utilities.js @@ -1,20 +1,32 @@ // Copyright 2018, 2019 Stanford University see LICENSE for license -export const isResourceWithValueTemplateRef = ( property ) => { - return Boolean( - ( property.hasOwnProperty('type') && property.type === 'resource' ) && - ( property.hasOwnProperty('valueConstraint') && - ( property.valueConstraint.hasOwnProperty('valueTemplateRefs') && - property.valueConstraint.valueTemplateRefs.length > 0)) - ) +const _ = require('lodash') + +export const isResourceWithValueTemplateRef = property => { + return property?.type === 'resource' && + property?.valueConstraint?.valueTemplateRefs?.length > 0 } -export const resourceToName = (uri) => { - try{ - return uri.substr(uri.lastIndexOf('/') + 1) - } catch { +export const resourceToName = uri => { + if (!_.isString(uri)) return undefined - } + + return uri.substr(uri.lastIndexOf('/') + 1) +} + + +export const defaultValuesFromPropertyTemplate = propertyTemplate => { + // Use safe navigation to deal with differently shaped property templates + const defaultValue = propertyTemplate?.valueConstraint?.defaults?.[0] + + if (!defaultValue) + return [] + + return [{ + id: defaultValue.defaultURI, + label: defaultValue.defaultLiteral, + uri: defaultValue.defaultURI + }] } export const templateBoolean = (value) => { diff --git a/src/components/editor/InputListLOC.jsx b/src/components/editor/InputListLOC.jsx index 40b0cb248..f044b267e 100644 --- a/src/components/editor/InputListLOC.jsx +++ b/src/components/editor/InputListLOC.jsx @@ -6,29 +6,27 @@ import PropTypes from 'prop-types' import PropertyRemark from './PropertyRemark' import { connect } from 'react-redux' import { changeSelections } from '../../actions/index' +import { defaultValuesFromPropertyTemplate } from '../../Utilities' import shortid from 'shortid' class InputListLOC extends Component { constructor(props) { super(props) - this.state = { - isLoading: false, - options: [], - defaults: [] - } + this.hasPropertyRemark = this.hasPropertyRemark.bind(this) - try { - const defaultValue = this.props.propertyTemplate.valueConstraint.defaults[0] - const defaults = [{ - id: defaultValue.defaultURI, - label: defaultValue.defaultLiteral, - uri: defaultValue.defaultURI - }] - this.state.defaults = defaults + const defaults = defaultValuesFromPropertyTemplate(this.props.propertyTemplate) + + if (defaults.length > 0) { this.setPayLoad(defaults) - } catch (error) { - console.error(`defaults not defined in the property template: ${error}`) + } else { + console.error(`no defaults defined in property template: ${JSON.stringify(this.props.propertyTemplate)}`) + } + + this.state = { + isLoading: false, + options: [], + defaults: defaults } } diff --git a/src/components/editor/InputLookupQA.jsx b/src/components/editor/InputLookupQA.jsx index 9f5617301..1f3fea982 100644 --- a/src/components/editor/InputLookupQA.jsx +++ b/src/components/editor/InputLookupQA.jsx @@ -6,197 +6,200 @@ import Swagger from 'swagger-client' import { connect } from 'react-redux' import { getProperty } from '../../reducers/index' import { changeSelections } from '../../actions/index' +import { defaultValuesFromPropertyTemplate } from '../../Utilities' -const AsyncTypeahead = asyncContainer( Typeahead ) +const AsyncTypeahead = asyncContainer(Typeahead) class InputLookupQA extends Component { - constructor( props ) { - super( props ) - this.state = { - isLoading: false - } - } + constructor(props) { + super(props) + + const defaults = defaultValuesFromPropertyTemplate(this.props.propertyTemplate) - //Render menu function to be used by typeahead - renderMenuFunc = ( results, menuProps ) => { - //Returning results per each promise - //If error is returned, it will be used to display for that source - const items = []; - let r, i, authLabel, resultsLength, authURI, headerKey, result; - resultsLength = results.length; - let idx = 0; - for ( i = 0; i < resultsLength; i++ ) { - result = results[i]; - authLabel = result.authLabel; - authURI = result.authURI; - headerKey = authURI + "-header"; - //Add header only if more than one authority request - if ( resultsLength > 1 ) - items.push( - {authLabel} - ); - //For this authority, display results - if ( "isError" in result ) { - //if error, then get error from within result and display that message - let errorMessage = "An error occurred in retrieving results"; - let errorHeaderKey = headerKey + "-error"; - items.push( - {errorMessage} - ); - } else { - //if not error, print out items for result - r = result.body; - r.forEach( function( result ) { - items.push( - {result.label} - ); - idx++; - } ); - //if the length of results is zero we need to show that as well - if ( r.length == 0 ) { - let noResultsMessage = "No results for this lookup"; - let noResultsHeaderKey = headerKey + "-noResults"; - items.push( - {noResultsMessage} - ); - } - - } - } - - return ( - - {items} - - ) + if (defaults.length === 0) + console.error(`no defaults defined in property template: ${JSON.stringify(this.props.propertyTemplate)}`) + + this.state = { + isLoading: false, + defaults: defaults + } + } + + // Render menu function to be used by typeahead + renderMenuFunc = (results, menuProps) => { + // Returning results per each promise + // If error is returned, it will be used to display for that source + const items = [] + let menuItemIndex = 0 + + results.forEach((result, _i, list) => { + const authLabel = result.authLabel + const headerKey = `${result.authURI}-header` + + if (list.length > 1) + items.push({authLabel}) + + if (result.isError) { + const errorMessage = 'An error occurred in retrieving results' + const errorHeaderKey = `${headerKey}-error` + items.push( + {errorMessage} + ) + + // Effectively a `continue`/`next` statement within the `forEach()` context, skipping to the next iteration + return + } + + const body = result.body + + if (body.length === 0) { + const noResultsMessage = 'No results for this lookup' + const noResultsHeaderKey = `${headerKey}-noResults` + + items.push( + {noResultsMessage} + ) + + // Effectively a `continue`/`next` statement within the `forEach()` context, skipping to the next iteration + return + } + + body.forEach(innerResult => { + items.push( + {innerResult.label} + ) + menuItemIndex++ + }) + }) + + return ( + + {items} + + ) + } + + render() { + let authority, subauthority, language + const lookupConfigs = this.props.lookupConfig + + const isMandatory = this.props.propertyTemplate.mandatory === undefined ? + true : + JSON.parse(this.props.propertyTemplate.mandatory) + const isRepeatable = this.props.propertyTemplate.repeatable === undefined ? + true : + JSON.parse(this.props.propertyTemplate.repeatable) + + const typeaheadProps = { + required: isMandatory, + multiple: isRepeatable, + placeholder: this.props.propertyTemplate.propertyLabel, + useCache: true, + isLoading: this.state.isLoading, + options: this.state.options, + selected: this.state.selected, + defaultSelected: this.state.defaults, + delay: 300 } - render() { - - let isMandatory, isRepeatable, authority, subauthority, language - const lookupConfigs = this.props.lookupConfig - - try { - isMandatory = JSON.parse( this.props.propertyTemplate.mandatory ) - isRepeatable = JSON.parse( this.props.propertyTemplate.repeatable ) - } catch ( error ) { - console.error( `Problem with properties fetched from resource template: ${error}` ) - } - const typeaheadProps = { - required: isMandatory, - multiple: isRepeatable, - placeholder: this.props.propertyTemplate.propertyLabel, - useCache: true, - isLoading: this.state.isLoading, - options: this.state.options, - selected: this.state.selected, - delay: 300 - } - - return ( -
- { - return ( this.renderMenuFunc( results, menuProps ) ); - } - } + return ( +
+ this.renderMenuFunc(results, menuProps)} - onSearch={query => { - this.setState( { isLoading: true } ); - Swagger( { url: "src/lib/apidoc.json" } ).then(( client ) => { + onSearch={query => { + this.setState({ isLoading: true }) + Swagger({ url: "src/lib/apidoc.json" }).then(client => { //create array of promises based on the lookup config array that is sent in - let lookupPromises = lookupConfigs.map( lookupConfig => { - authority = lookupConfig.authority; - subauthority = lookupConfig.authority; - language = lookupConfig.language; - //return the 'promise' - //Since we don't want promise.all to fail if - //one of the lookups fails, we want a catch statement - //at this level which will then return the error - return client - .apis - .SearchQuery - .GET_searchAuthority( { - q: query, - vocab: authority, - subauthority: subauthority, - maxRecords: 8, - lang: language - } ) - .catch( function( err ) { - console.error( "Error in executing lookup against source", err ); - //return information along with the error in its own object - return { "isError": true, "errorObject": err }; - } ); - - } ); - - Promise.all( lookupPromises ).then(( values ) => { - - let valuesLength = values.length; - let i; - for ( i = 0; i < valuesLength; i++ ) { - //If undefined, add info - note if error, error object returned in object - //which allows attaching label and uri for authority - if ( values[i] ) { - values[i]["authLabel"] = lookupConfigs[i].label; - values[i]["authURI"] = lookupConfigs[i].uri; - } + const lookupPromises = lookupConfigs.map(lookupConfig => { + authority = lookupConfig.authority + subauthority = lookupConfig.authority + language = lookupConfig.language + //return the 'promise' + //Since we don't want promise.all to fail if + //one of the lookups fails, we want a catch statement + //at this level which will then return the error + return client + .apis + .SearchQuery + .GET_searchAuthority({ + q: query, + vocab: authority, + subauthority: subauthority, + maxRecords: 8, + lang: language + }) + .catch(function(err) { + console.error("Error in executing lookup against source", err) + //return information along with the error in its own object + return { isError: true, errorObject: err } + }) + }) + + Promise.all(lookupPromises).then(values => { + let i + for (i = 0; i < values.length; i++) { + //If undefined, add info - note if error, error object returned in object + //which allows attaching label and uri for authority + if (values[i]) { + values[i]["authLabel"] = lookupConfigs[i].label + values[i]["authURI"] = lookupConfigs[i].uri } - - this.setState( { - isLoading: false, - options: values - } ) - } - ) - } ).catch(() => { return false } ) - }} - onChange={selected => { - let payload = { + } + + this.setState( { + isLoading: false, + options: values + }) + }) + }).catch(() => false) + }} + + onChange={selected => { + const payload = { uri: this.props.propertyTemplate.propertyURI, items: selected, reduxPath: this.props.reduxPath + } + this.props.handleSelectedChange(payload) } - this.props.handleSelectedChange( payload ) - } - } - {...typeaheadProps} - - filterBy={() => { - /** Currently don't want any default filtering as we want all the results returned from QA, also we are passing in a complex object **/ - /* Your own filtering code goes here. */ - return true; - }} - /> -
- ) - } + } + + {...typeaheadProps} + + filterBy={() => { + /** Currently don't want any default filtering as we want all the results returned from QA, also we are passing in a complex object **/ + /* Your own filtering code goes here. */ + return true + }} + /> +
+ ) + } } InputLookupQA.propTypes = { - propertyTemplate: PropTypes.shape( { - propertyLabel: PropTypes.string, - mandatory: PropTypes.oneOfType( [PropTypes.string, PropTypes.bool] ), - repeatable: PropTypes.oneOfType( [PropTypes.string, PropTypes.bool] ), - valueConstraint: PropTypes.shape( { - useValuesFrom: PropTypes.oneOfType( [PropTypes.string, PropTypes.array] ) - } ) - } ).isRequired, - reduxPath: PropTypes.oneOfType([PropTypes.string, PropTypes.array]) + propertyTemplate: PropTypes.shape({ + propertyLabel: PropTypes.string, + mandatory: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + repeatable: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + valueConstraint: PropTypes.shape({ + useValuesFrom: PropTypes.oneOfType([PropTypes.string, PropTypes.array]) + }) + }).isRequired, + reduxPath: PropTypes.oneOfType([PropTypes.string, PropTypes.array]) } -const mapStateToProps = ( state, props ) => { +const mapStateToProps = (state, props) => { const result = getProperty(state, props) - return {selected: result} + return { selected: result } } -const mapDispatchToProps = dispatch => ( { - handleSelectedChange( selected ) { - dispatch( changeSelections( selected ) ) - } -} ) +const mapDispatchToProps = dispatch => ({ + handleSelectedChange(selected) { + dispatch(changeSelections(selected)) + } +}) -export default connect( mapStateToProps, mapDispatchToProps )( InputLookupQA ) +export default connect(mapStateToProps, mapDispatchToProps)(InputLookupQA) diff --git a/src/components/editor/PropertyComponent.jsx b/src/components/editor/PropertyComponent.jsx index 8b92e559f..6213e993a 100644 --- a/src/components/editor/PropertyComponent.jsx +++ b/src/components/editor/PropertyComponent.jsx @@ -7,17 +7,27 @@ import InputLookupQA from './InputLookupQA' import shortid from 'shortid' import lookupConfig from '../../../static/spoofedFilesFromServer/fromSinopiaServer/lookupConfig.json' import PropTypes from 'prop-types' -const _ = require('lodash') export class PropertyComponent extends Component { constructor(props) { super(props) this.state = { - configuration: getLookupConfigItems(this.props.propertyTemplate) + configuration: this.getLookupConfigItems(this.props.propertyTemplate) } } - inputComponentType = (property) => { + getLookupConfigItems = propertyTemplate => { + const vocabUriList = propertyTemplate?.valueConstraint?.useValuesFrom + + if (vocabUriList === undefined || vocabUriList.length === 0) + return [] + + const templateConfigItems = lookupConfig.filter(configItem => vocabUriList.includes(configItem.uri)) + + return templateConfigItems + } + + inputComponentType = property => { let config, result // We do not support mixed list and lookups, so we will just go with the value of the first config item found @@ -67,22 +77,6 @@ export class PropertyComponent extends Component { } } -export const getLookupConfigItems = (property) => { - const templateConfigItems = [] - - if (_.find([property], 'valueConstraint.useValuesFrom')) { - property.valueConstraint.useValuesFrom.map(templateUri => { - lookupConfig.map(configItem => { - if (configItem.uri === templateUri) { - templateConfigItems.push(configItem) - } - }) - }) - } - - return templateConfigItems -} - PropertyComponent.propTypes = { propertyTemplate: PropTypes.shape({ propertyLabel: PropTypes.string,