@@ -85,44 +85,69 @@ const isNilOrEmpty = either(isNil, isEmpty);
8585const _createOrMerge = ( dataset , fields , acteeId , publish ) => sql `
8686${ _insertDatasetDef ( dataset , acteeId , true , publish ) }
8787${ isNilOrEmpty ( fields ) ? sql `` : _insertProperties ( fields , publish ) }
88- SELECT "action" FROM ds
88+ SELECT "action", "id" FROM ds
8989` ;
9090
91- // Creates or merges dataset, properties and field mapping.
92- // Expects dataset:Frame auxed with `formDefId`, `schemaId`,
93- // and array of field:Frame auxed with `propertyName`
94- const createOrMerge = ( dataset , fields , publish = false ) => ( { one, Actees, Datasets, Projects } ) =>
95- Promise . all ( [
96- Projects . getById ( dataset . projectId ) . then ( ( o ) => o . get ( ) ) ,
97- Datasets . get ( dataset . projectId , dataset . name )
98- ] )
99- . then ( ( [ project , datasetOption ] ) =>
100- ( datasetOption . isDefined ( )
101- ? datasetOption . get ( ) . acteeId
102- : Actees . provision ( 'dataset' , project ) . then ( ( actee ) => ( actee . id ) ) ) )
103- . then ( ( acteeId ) =>
104- one ( _createOrMerge ( dataset , fields , acteeId , publish ) )
105- . catch ( error => {
106- if ( error . constraint === 'ds_property_fields_dspropertyid_formdefid_unique' ) {
107- throw Problem . user . invalidEntityForm ( { reason : 'Multiple Form Fields cannot be saved to a single Dataset Property.' } ) ;
108- }
109- throw error ;
110- } ) )
111- . then ( ( result ) => Datasets . get ( dataset . projectId , dataset . name ) . then ( ( ds ) => ds . get ( ) )
112- . then ( ( ds ) => ( ( publish === true )
113- ? Datasets . getProperties ( ds . id ) . then ( properties => ( { ...ds , properties } ) )
114- : ds . with ( { action : result . action } ) ) ) ) ;
115-
116- createOrMerge . audit = ( dataset , _ , fields , publish ) => ( log ) =>
91+ // Takes the dataset name and field->property mappings defined in a form
92+ // and creates or updates the named dataset.
93+ // Arguments:
94+ // * dataset name
95+ // * form (a Form frame or object containing projectId, formDefId, and schemaId)
96+ // * array of field objects and property names that were parsed from the form xml
97+ // * publish flag saying whether or not to immediately publish the dataset
98+ const createOrMerge = ( name , form , fields , publish ) => async ( { one, Actees, Datasets, Projects } ) => {
99+ const { projectId } = form ;
100+ const { id : formDefId , schemaId } = form . def ;
101+
102+ // Provision acteeId if necessary.
103+ // Need to check for existing dataset, and if not found, need to also
104+ // fetch the project since the dataset will be an actee child of the project.
105+ const existingDataset = await Datasets . get ( projectId , name ) ;
106+ const acteeId = existingDataset . isDefined ( )
107+ ? existingDataset . get ( ) . acteeId
108+ : await Projects . getById ( projectId ) . then ( ( o ) => o . get ( ) )
109+ . then ( ( project ) => Actees . provision ( 'dataset' , project ) )
110+ . then ( ( actee ) => ( actee . id ) ) ;
111+ const partial = new Dataset ( { name, projectId, acteeId } , { formDefId } ) ;
112+
113+ // Prepare dataset properties from form fields:
114+ // Step 1: Filter to only fields with property name binds
115+ let dsPropertyFields = fields . filter ( ( field ) => ( field . propertyName ) ) ;
116+ // Step 2: Disallow properties to be named "name" or "label"
117+ if ( dsPropertyFields . filter ( ( field ) => field . propertyName === 'name' || field . propertyName === 'label' ) . length > 0 )
118+ throw Problem . user . invalidEntityForm ( { reason : 'Invalid Dataset property.' } ) ;
119+ // Step 3: Build Form Field frames to handle dataset property field insertion
120+ dsPropertyFields = dsPropertyFields . map ( ( field ) => new Form . Field ( field , { propertyName : field . propertyName , schemaId, formDefId } ) ) ;
121+
122+ // Insert or update: update dataset, dataset properties, and links to fields and current form
123+ // result contains action (created or updated) and the id of the dataset.
124+ const result = await one ( _createOrMerge ( partial , dsPropertyFields , acteeId , publish ) )
125+ . catch ( error => {
126+ if ( error . constraint === 'ds_property_fields_dspropertyid_formdefid_unique' ) {
127+ throw Problem . user . invalidEntityForm ( { reason : 'Multiple Form Fields cannot be saved to a single Dataset Property.' } ) ;
128+ }
129+ throw error ;
130+ } ) ;
131+
132+ // Fetch all published properties for this dataset, even beyond what is defined in this form.
133+ const publishedProperties = ( ( publish === true )
134+ ? await Datasets . getProperties ( result . id )
135+ : null ) ;
136+
137+ // Partial contains acteeId which is needed for auditing.
138+ // Additional form fields and properties needed for audit logging as well.
139+ return partial . with ( { action : result . action , fields : dsPropertyFields , properties : publishedProperties } ) ;
140+ } ;
141+
142+ createOrMerge . audit = ( dataset , name , form , fields , publish ) => ( log ) =>
117143 ( ( dataset . action === 'created' )
118- ? log ( 'dataset.create' , dataset , { fields : fields . map ( ( f ) => [ f . path , f . propertyName ] ) } )
119- : log ( 'dataset.update' , dataset , { fields : fields . map ( ( f ) => [ f . path , f . propertyName ] ) } ) )
144+ ? log ( 'dataset.create' , dataset , { fields : dataset . fields . map ( ( f ) => [ f . path , f . propertyName ] ) } )
145+ : log ( 'dataset.update' , dataset , { fields : dataset . fields . map ( ( f ) => [ f . path , f . propertyName ] ) } ) )
120146 . then ( ( ) => ( ( publish === true )
121147 ? log ( 'dataset.update.publish' , dataset , { properties : dataset . properties . map ( p => p . name ) } )
122148 : null ) ) ;
123149createOrMerge . audit . withResult = true ;
124150
125-
126151////////////////////////////////////////////////////////////////////////////
127152// DATASET (AND DATASET PROPERTY) PUBLISH
128153
0 commit comments