Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 43 additions & 44 deletions lib/editor/util/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export type EditorValidationIssue = {
reason: string
}

const EMPTY_FIELD_REASON = 'Required field must not be empty'

export function doesNotExist (value: any): boolean {
return value === '' || value === null || typeof value === 'undefined'
}
Expand All @@ -39,7 +41,6 @@ export function validate (
const isOptionalAndEmpty = !required && valueDoesNotExist
const agencies = getTableById(tableData, 'agency')
let locationType: ?number = null
let reason = 'Required field must not be empty'

// entity.locationtype is a string. Convert to number for conditinals later on.
if (entity && entity.location_type !== null) {
Expand All @@ -52,13 +53,21 @@ export function validate (
result: false | EditorValidationIssue
}

/**
* Construct an EditorValidationIssue for the field name and reason (defaults to
* empty field message).
*/
function validationIssue (reason = EMPTY_FIELD_REASON, field = name) {
return {field, invalid: true, reason}
}

/**
* Checks whether value is a positive number
*/
function checkPositiveNumber (): CheckPositiveOutput {
if (isRequiredButEmpty) {
return {
result: {field: name, invalid: isRequiredButEmpty, reason}
result: validationIssue()
}
} else if (isOptionalAndEmpty) {
return {
Expand All @@ -70,14 +79,14 @@ export function validate (
// make sure value is parseable to a number
if (isNaN(num)) {
return {
result: {field: name, invalid: true, reason: 'Field must be a valid number'}
result: validationIssue('Field must be a valid number')
}
}

// make sure value is positive
if (num < 0) {
return {
result: {field: name, invalid: true, reason: 'Field must be a positive number'}
result: validationIssue('Field must be a positive number')
}
}

Expand Down Expand Up @@ -112,21 +121,20 @@ export function validate (
idList.length > 1 &&
valueDoesNotExist
) {
reason = 'Identifier is required if more than one agency exists'
return {field: name, invalid: isRequiredButEmpty, reason}
return validationIssue('Identifier is required if more than one agency exists')
}
if (isRequiredButEmpty || isNotUnique) {
let reason
if (isNotUnique) {
reason = 'Identifier must be unique'
}
return {field: name, invalid: isRequiredButEmpty || isNotUnique, reason}
return validationIssue(reason)
} else {
return false
}
case 'TEXT':
if (name === 'stop_name' && locationType !== null && (typeof locationType === 'number' && locationType <= 2)) {
reason = 'Stop name is required for your current location type'
return {field: name, invalid: isOptionalAndEmpty, reason}
return validationIssue('Stop name is required for stop, station, and entrance location types.')
}
if (name === 'route_short_name' && !value && entity && entity.route_long_name) {
return false
Expand All @@ -139,7 +147,7 @@ export function validate (
return false
} else {
if (isRequiredButEmpty) {
return {field: name, invalid: isRequiredButEmpty, reason}
return validationIssue()
} else {
return false
}
Expand All @@ -150,82 +158,77 @@ export function validate (
case 'GTFS_FARE':
case 'GTFS_SERVICE':
if (isRequiredButEmpty) {
return {field: name, invalid: isRequiredButEmpty, reason}
return validationIssue()
} else {
return false
}
case 'URL':
const isNotUrl = value && !validator.isURL(value)
if (isRequiredButEmpty || isNotUrl) {
let reason
if (isNotUrl) {
reason = 'Field must contain valid URL.'
}
return {field: name, invalid: isRequiredButEmpty || isNotUrl, reason}
return validationIssue(reason)
} else {
return false
}
case 'EMAIL':
const isNotEmail = value && !validator.isEmail(value)
if (isRequiredButEmpty || isNotEmail) {
let reason
if (isNotEmail) {
reason = 'Field must contain valid email address.'
}
return {field: name, invalid: isRequiredButEmpty || isNotEmail, reason}
return validationIssue(reason)
} else {
return false
}
case 'GTFS_ZONE':
if (isRequiredButEmpty) {
return {field: name, invalid: isRequiredButEmpty, reason}
return validationIssue()
} else {
return false
}
case 'TIMEZONE':
if (isRequiredButEmpty) {
return {field: name, invalid: isRequiredButEmpty, reason}
return validationIssue()
} else {
return false
}
case 'LANGUAGE':
if (isRequiredButEmpty) {
return {field: name, invalid: isRequiredButEmpty, reason}
return validationIssue()
} else {
return false
}
case 'LATITUDE':
const isNotLat = value > 90 || value < -90
if (isNotLat) {
reason = 'Field must be valid latitude.'
return {field: name, invalid: isNotLat, reason}
return validationIssue('Field must be valid latitude.')
}
if (isOptionalAndEmpty && locationType !== null && (typeof locationType === 'number' && locationType <= 2)) {
reason = 'Latitude and Longitude are required for your current location type'
return {field: name, invalid: isOptionalAndEmpty, reason}
return validationIssue('Latitude and Longitude are required for your current location type')
}
return false
case 'LONGITUDE':
const isNotLng = value > 180 || value < -180
if (isNotLng) {
reason = 'Field must be valid longitude.'
return {field: name, invalid: isOptionalAndEmpty || isNotLng, reason}
return validationIssue('Field must be valid longitude.')
}
if (isOptionalAndEmpty && locationType !== null && (typeof locationType === 'number' && locationType <= 2)) {
reason = 'Latitude and Longitude are required for your current location type'
return {field: name, invalid: isOptionalAndEmpty || isNotLng, reason}
if (isOptionalAndEmpty && typeof locationType === 'number' && locationType <= 2) {
return validationIssue('Latitude and Longitude are required for your current location type')
}
return false
case 'TIME':
case 'NUMBER':
const isNotANumber = isNaN(value)
if (isRequiredButEmpty || isNotANumber) {
let reason
if (isNotANumber) {
reason = 'Field must be valid number'
}
return {
field: name,
invalid: isRequiredButEmpty || isNotANumber,
reason
}
return validationIssue(reason)
} else {
return false
}
Expand All @@ -247,8 +250,7 @@ export function validate (
}
if (!hasService && name === 'monday') {
// only add validation issue for one day of week (monday)
reason = 'Calendar must have service for at least one day'
return {field: name, invalid: isRequiredButEmpty, reason}
return validationIssue('Calendar must have service for at least one day')
}
return false
case 'DROPDOWN':
Expand All @@ -257,7 +259,7 @@ export function validate (
field.options &&
field.options.findIndex(o => o.value === '') === -1
) {
return {field: name, invalid: isRequiredButEmpty, reason}
return validationIssue()
} else {
return false
}
Expand All @@ -267,11 +269,7 @@ export function validate (
agencies.length > 1
) {
if (valueDoesNotExist) {
return {
field: name,
invalid: true,
reason: 'Field must be populated for feeds with more than one agency.'
}
return validationIssue('Field must be populated for feeds with more than one agency.')
}
}
return false
Expand Down Expand Up @@ -300,18 +298,19 @@ export function validate (
}
}
if (!value || value.length === 0) {
return {field: `dates`, invalid: true, reason}
return validationIssue(EMPTY_FIELD_REASON, 'dates')
}
// check if date already exists in this or other exceptions
for (let i = 0; i < value.length; i++) {
const dateItemName = `dates-${i}`
if (dateMap[value[i]] && dateMap[value[i]].length > 1) {
// eslint-disable-next-line standard/computed-property-even-spacing
reason = `Date (${value[
const reason = `Date (${value[
i
]}) cannot appear more than once for all exceptions`
return {field: `dates-${i}`, invalid: true, reason}
return validationIssue(reason, dateItemName)
} else if (!moment(value[i], 'YYYYMMDD', true).isValid()) {
return {field: `dates-${i}`, invalid: true, reason}
return validationIssue(EMPTY_FIELD_REASON, dateItemName)
}
}
return false
Expand All @@ -331,7 +330,7 @@ export function validate (
)
)
) {
return {field: name, invalid: true, reason: 'Field must be a positive integer'}
return validationIssue('Field must be a positive integer')
}
return false
case 'POSITIVE_NUM':
Expand All @@ -342,7 +341,7 @@ export function validate (
case 'COLOR':
default:
if (isRequiredButEmpty) {
return {field: name, invalid: isRequiredButEmpty, reason}
return validationIssue()
}
return false
}
Expand Down