All files / lib/query log-warnings.js

16.66% Statements 4/24
0% Branches 0/34
0% Functions 0/6
16.66% Lines 4/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 661x 1x 1x                                                                                                                           1x  
const _ = require('lodash')
const { getDataTypeFromValue } = require('../helpers')
const chalk = require('chalk')
 
function logWarnings (geojson, format) {
  const { metadata = {}, features } = geojson
  const esriFormat = format !== geojson
 
  if (esriFormat && !metadata.idField) {
    console.warn(chalk.yellow('WARNING: requested provider has no "idField" assignment. You will get the most reliable behavior from ArcGIS clients if the provider assigns the "idField" to a property that is an unchanging 32-bit integer. Koop will create an OBJECTID field in the absence of an "idField" assignment.'))
  }
 
  if (esriFormat && hasMixedCaseObjectIdKey(metadata.idField)) {
    console.warn(chalk.yellow('WARNING: requested provider\'s "idField" is a mixed-case version of "OBJECTID". This can cause errors in ArcGIS clients.'))
  }
 
  // Compare provider metadata fields to feature properties
  // TODO: refactor
  if (metadata.fields && _.has(features, '[0].properties')) {
    warnOnMetadataFieldDiscrepancies(geojson.metadata.fields, geojson.features[0].properties)
  }
}
 
function hasMixedCaseObjectIdKey (idField = '') {
  return idField.toLowerCase() === 'objectid' && idField !== 'OBJECTID'
}
 
/**
 * Compare fields generated from metadata to properties of a data feature.
 * Warn if differences discovered
 * @param {*} metadataFields
 * @param {*} properties
 */
function warnOnMetadataFieldDiscrepancies (metadataFields, featureProperties) {
  // build a comparison collection from the data samples properties
  const featureFields = Object.keys(featureProperties).map(name => {
    return {
      name,
      type: getDataTypeFromValue(featureProperties[name])
    }
  })
 
  // compare metadata to feature properties; identifies fields defined in metadata that are not found in feature properties
  // or that have a metadata type definition inconsistent with feature property's value
  metadataFields.forEach(field => {
    // look for a defined field in the features properties
    const featureField = _.find(featureFields, ['name', field.name]) || _.find(featureFields, ['name', field.alias])
    if (!featureField || (field.type !== featureField.type && !(field.type === 'Date' && featureField.type === 'Integer') && !(field.type === 'Double' && featureField.type === 'Integer'))) {
      console.warn(chalk.yellow(`WARNING: requested provider's metadata field "${field.name} (${field.type})" not found in feature properties)`))
    }
  })
 
  // compare feature properties to metadata fields; identifies fields found on feature that are not defined in metadata field array
  featureFields.forEach(field => {
    const noNameMatch = _.find(metadataFields, ['name', field.name])
    const noAliasMatch = _.find(metadataFields, ['alias', field.name])
 
    // Exclude warnings on feature fields named OBJECTID because OBJECTID may have been added by winnow in which case it should not be in the metadata fields array
    if (!(noNameMatch || noAliasMatch) && field.name !== 'OBJECTID') {
      console.warn(chalk.yellow(`WARNING: requested provider's features have property "${field.name} (${field.type})" that was not defined in metadata fields array)`))
    }
  })
}
 
module.exports = { logWarnings }