diff --git a/.github/workflows/node-ci.yml b/.github/workflows/node-ci.yml index 3a9f736ff..361b8a54f 100644 --- a/.github/workflows/node-ci.yml +++ b/.github/workflows/node-ci.yml @@ -74,9 +74,9 @@ jobs: - name: Run e2e tests if: inputs.e2e run: | - (echo "E2E Test Attempt 1" && docker-compose --env-file __tests__/e2e/.env -f __tests__/e2e/docker-compose.yml up --abort-on-container-exit) || \ - (echo "E2E Test Attempt 2" && docker-compose --env-file __tests__/e2e/.env -f __tests__/e2e/docker-compose.yml up --abort-on-container-exit) || \ - (echo "E2E Test Attempt 3" && docker-compose --env-file __tests__/e2e/.env -f __tests__/e2e/docker-compose.yml up --abort-on-container-exit) || \ + (echo "E2E Test Attempt 1" && docker compose --env-file __tests__/e2e/.env -f __tests__/e2e/docker-compose.yml up --abort-on-container-exit) || \ + (echo "E2E Test Attempt 2" && docker compose --env-file __tests__/e2e/.env -f __tests__/e2e/docker-compose.yml up --abort-on-container-exit) || \ + (echo "E2E Test Attempt 3" && docker compose --env-file __tests__/e2e/.env -f __tests__/e2e/docker-compose.yml up --abort-on-container-exit) || \ (echo "E2E Tests Failed" && exit 1) # At this point, the build is successful. - name: Semantic Release diff --git a/README.md b/README.md index c878c1bc9..b991f1123 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A pre-configured datatools instance can be lauched via Docker by running ```bash cd docker cp ../configurations/default/env.yml.tmp ../configurations/default/env.yml -docker-compose up +docker compose up ``` from the datatools-ui directory. Datatools will then be running on port `9966`. diff --git a/docs/dev/development.md b/docs/dev/development.md index d124e9722..b12c7c261 100644 --- a/docs/dev/development.md +++ b/docs/dev/development.md @@ -47,6 +47,6 @@ yarn start -- --config /path/to/config ## E2E tests -The e2e tests have been Dockerized, which allows them to be run easily anywhere `docker-compose` works. To run them on localhost, first create a `.env` file in the `__tests__/e2e`. `docker-compose` will alert you as to which variables must be present. +The e2e tests have been Dockerized, which allows them to be run easily anywhere `docker compose` works. To run them on localhost, first create a `.env` file in the `__tests__/e2e`. `docker compose` will alert you as to which variables must be present. -To run the tests, run `docker-compose -f docker-compose.yml up --abort-on-container-exit` in the `__tests__/e2e/` directory. +To run the tests, run `docker compose -f docker compose.yml up --abort-on-container-exit` in the `__tests__/e2e/` directory. diff --git a/i18n/english.yml b/i18n/english.yml index ee3ddcfb3..9ffdd9e6a 100644 --- a/i18n/english.yml +++ b/i18n/english.yml @@ -135,49 +135,6 @@ components: danger: Danger! success: Success! warning: Warning! - DeploymentSettings: - boundsPlaceholder: 'min_lon, min_lat, max_lon, max_lat' - buildConfig: - elevationBucket: - accessKey: Access Key - bucketName: S3 Bucket Name - secretKey: Secret Key - fares: Fares - fetchElevationUS: Fetch Elevation - save: Save - stationTransfers: Sta. Transfers - subwayAccessTime: Subway Access Time - title: Build Config - clear: Clear - manageServers: Manage deployment servers - osm: - bounds: Custom Extract Bounds - custom: Use Custom Extract Bounds - gtfs: Use GTFS-Derived Extract Bounds - title: OSM Extract - routerConfig: - brandingUrlRoot: Branding URL Root - carDropoffTime: Car Dropoff Time - driveDistanceReluctance: Drive Distance Reluctance - numItineraries: '# of itineraries' - requestLogFile: Request log file - stairsReluctance: Stairs Reluctance - title: Router Config - itineraryFilters: - nonTransitGeneralizedCostLimit: Non-Transit Generalized Cost Limit - updaters: - $index: - feedId: Feed ID - frequencySec: Frequency (in seconds) - sourceType: Source type - type: Type - url: URL - new: Add updater - title: Real-time updaters - placeholder: Updater name - walkSpeed: Walk Speed - save: Save - title: Deployment DeploymentVersionsTable: dateRetrieved: Date Retrieved errorCount: Error Count @@ -578,7 +535,6 @@ components: custom: Use Custom Extract Bounds gtfs: Use GTFS-Derived Extract Bounds title: OSM Extract - title: Deployment routerConfig: brandingUrlRoot: Branding URL Root carDropoffTime: Car Dropoff Time @@ -935,66 +891,6 @@ components: noAccess: No Access permissions: Permissions title: Project Settings for - ProjectSettings: - deployment: - buildConfig: - elevationBucket: - accessKey: Access Key - bucketName: S3 Bucket Name - secretKey: Secret Key - fares: Fares - fetchElevationUS: Fetch Elevation - stationTransfers: Sta. Transfers - subwayAccessTime: Subway Access Time - title: Build Config - osm: - bounds: Custom Extract Bounds - custom: Use Custom Extract Bounds - gtfs: Use GTFS-Derived Extract Bounds - title: OSM Extract - otpServers: - $index: - admin: Admin access only? - delete: Remove - internalUrl: Internal URLs - name: Name - namePlaceholder: Production - publicUrl: Public URL - r5: R5 Server? - s3Bucket: S3 bucket name - targetGroupArn: Target Group ARN - new: Add server - serverPlaceholder: Server name - title: Servers - routerConfig: - brandingUrlRoot: Branding URL Root - carDropoffTime: Car Dropoff Time - driveDistanceReluctance: Drive Distance Reluctance - numItineraries: '# of itineraries' - requestLogFile: Request log file - stairsReluctance: Stairs Reluctance - title: Router Config - itineraryFilters: - nonTransitGeneralizedCostLimit: Non-Transit Generalized Cost Limit - updaters: - $index: - feedId: Feed ID - frequencySec: Frequency (in seconds) - sourceType: Source type - type: Type - url: URL - new: Add updater - title: Real-time updaters - placeholder: Updater name - walkSpeed: Walk Speed - title: Deployment - project: - cannotFetchFeeds: Cannot fetch feeds - feeds: Feeds - permissions: Permissions - rename: Rename - save: Save - title: Settings ProjectSettingsForm: cancel: Cancel confirmDelete: Are you sure you want to delete this project? This action cannot be undone and all feed sources and their versions will be permanently deleted. diff --git a/i18n/german.yml b/i18n/german.yml index fbdbca111..672d7640b 100644 --- a/i18n/german.yml +++ b/i18n/german.yml @@ -136,46 +136,6 @@ components: danger: Gefahr! success: Erfolg! warning: Warnung! - DeploymentSettings: - boundsPlaceholder: min_lon, min_lat, max_lon, max_lat - buildConfig: - elevationBucket: - accessKey: Zugangs-Schlüssel - bucketName: S3 Bucket Name - secretKey: Secret Key - fares: Tarife - fetchElevationUS: Höhenmodell abrufen - save: Speichern - stationTransfers: Umstiege - subwayAccessTime: U-Bahn-Zugangszeiten - title: Build-Konfiguration - clear: Zurücksetzen - manageServers: Deployment-Server verwalten - osm: - bounds: Selbst-definierte Extrakt-Grenzen - custom: Benutze selbst-definierte Extrakt-Grenzen - gtfs: Grenzen aus GTFS ableiten - title: OSM-Extrakt - routerConfig: - brandingUrlRoot: Branding-URL Wurzel - carDropoffTime: PKW-Rückgabe-Zeit - numItineraries: '# of Reisevorschläge' - requestLogFile: Log-Datei anfordern - stairsReluctance: Treppen-Abneigung - title: Router Konfiguration - updaters: - $index: - defaultAgencyId: Standard-Betreiber ID - frequencySec: Frequenz (in Sekunden) - sourceType: Quellen-Typ - type: Typ - url: URL - new: Aktualisierer hinzufügen - placeholder: Aktualisierer-Name - title: Echtzeit-Aktualisierungs-Updater - walkSpeed: Geh-Geschwindigkeit - save: Speichern - title: Deployment DeploymentVersionsTable: dateRetrieved: Abrufdatum errorCount: Fehlerzahl @@ -1014,63 +974,6 @@ components: transitland: Sync von transit.land url: Feed-Abruf URL version: Version - ProjectSettings: - deployment: - buildConfig: - elevationBucket: - accessKey: Zugriffs-Schlüssel (access) - bucketName: S3 Bucket Name - secretKey: Geheim-Schlüssel (secret) - fares: Tarife - fetchElevationUS: Rufe Geländemodell ab - stationTransfers: Umstiege - subwayAccessTime: U-Bahn-Zugangszeiten - title: Build Konfiguration - osm: - bounds: Benutzerdefinierte Extraktions-Grenzen - custom: Benutzerdefinierte Extraktions-Grenzen verwenden - gtfs: Benutzerdefinierte Extraktions-Grenzen verwenden - title: OSM Extrakt - otpServers: - $index: - admin: Nur Admin-Zugriff? - delete: Entfernen - internalUrl: Interne URLs - name: Name - namePlaceholder: Produktion - publicUrl: Öffentliche URL - r5: R5 Server? - s3Bucket: S3 Bucket Name - targetGroupArn: Zielgruppen ARN - new: Server hinzufügen - serverPlaceholder: Server-Name - title: Server - routerConfig: - brandingUrlRoot: Branding-URL Wurzel - carDropoffTime: PKW-Abstell-Zeit - numItineraries: '# Reisevorschläge' - requestLogFile: Log-Datei anfordern - stairsReluctance: Treppen-Vermeidung - title: Router Konfiguration - updaters: - $index: - defaultAgencyId: Default Betreiber-ID - frequencySec: Frequenz (in Sekunden) - sourceType: Quellen-Typ - type: Typ - url: URL - new: Updater hinzufügen - placeholder: Updater-Name - title: Real-time Updater - walkSpeed: Lauf-Geschwindigkeit - title: Deployment - project: - cannotFetchFeeds: Feeds können nicht abgerufen werden - feeds: Feeds - permissions: Berechtigungen - rename: Umbennenen - save: Speichern - title: Einstellungen ProjectSettingsForm: cancel: Abbrechen confirmDelete: Sind Sie sicher, dass Sie dieses Projekt löschen möchten? Diese diff --git a/i18n/polish.yml b/i18n/polish.yml index f3e9f8e05..7b2ef79cd 100644 --- a/i18n/polish.yml +++ b/i18n/polish.yml @@ -139,46 +139,6 @@ components: danger: Uwaga! success: Sukces! warning: Ostrzeżenie! - DeploymentSettings: - boundsPlaceholder: min_lon, min_lat, max_lon, max_lat - buildConfig: - elevationBucket: - accessKey: Access Key - bucketName: S3 Bucket Name - secretKey: Secret Key - fares: Taryfy - fetchElevationUS: Fetch Elevation - save: Zapisz - stationTransfers: Transfery na stacji - subwayAccessTime: Godziny otwarcia metra - title: Build Config - clear: Wyczyść - manageServers: Manage deployment servers - osm: - bounds: Custom Extract Bounds - custom: Use Custom Extract Bounds - gtfs: Use GTFS-Derived Extract Bounds - title: Wyciąg z OSM - routerConfig: - brandingUrlRoot: Branding URL Root - carDropoffTime: Car Dropoff Time - numItineraries: '# of itineraries' - requestLogFile: Request log file - stairsReluctance: Stairs Reluctance - title: Router Config - updaters: - $index: - defaultAgencyId: Domyślny ID przewoźnika - frequencySec: Częstotliwość (w sekundach) - sourceType: Rodzaj źródła - type: Typ - url: URL - new: Add updater - placeholder: Updater name - title: Real-time updaters - walkSpeed: Walk Speed - save: Zapisz - title: Wdrożenie DeploymentVersionsTable: dateRetrieved: Data pozyskania errorCount: Licznik błędów @@ -1003,63 +963,6 @@ components: transitland: Sync from transit.land url: Feed download URL version: version - ProjectSettings: - deployment: - buildConfig: - elevationBucket: - accessKey: Access Key - bucketName: S3 Bucket Name - secretKey: Secret Key - fares: Fares - fetchElevationUS: Fetch Elevation - stationTransfers: Sta. Transfers - subwayAccessTime: Subway Access Time - title: Build Config - osm: - bounds: Custom Extract Bounds - custom: Use Custom Extract Bounds - gtfs: Use GTFS-Derived Extract Bounds - title: OSM Extract - otpServers: - $index: - admin: Admin access only? - delete: Remove - internalUrl: Internal URLs - name: Name - namePlaceholder: Production - publicUrl: Public URL - r5: R5 Server? - s3Bucket: S3 bucket name - targetGroupArn: Target Group ARN - new: Add server - serverPlaceholder: Server name - title: Servers - routerConfig: - brandingUrlRoot: Branding URL Root - carDropoffTime: Car Dropoff Time - numItineraries: '# of itineraries' - requestLogFile: Request log file - stairsReluctance: Stairs Reluctance - title: Router Config - updaters: - $index: - defaultAgencyId: Default agency ID - frequencySec: Frequency (in seconds) - sourceType: Source type - type: Type - url: URL - new: Add updater - placeholder: Updater name - title: Real-time updaters - walkSpeed: Walk Speed - title: Deployment - project: - cannotFetchFeeds: Cannot fetch feeds - feeds: Feeds - permissions: Permissions - rename: Rename - save: Save - title: Settings ProjectSettingsForm: cancel: Cancel confirmDelete: Are you sure you want to delete this project? This action cannot diff --git a/lib/manager/components/ProjectSettings.js b/lib/manager/components/ProjectSettings.js index 62bc9e474..5f82eb748 100644 --- a/lib/manager/components/ProjectSettings.js +++ b/lib/manager/components/ProjectSettings.js @@ -1,22 +1,15 @@ // @flow import React, { Component } from 'react' -import { Row, Col, Panel, ListGroup, ListGroupItem } from 'react-bootstrap' -import { LinkContainer } from 'react-router-bootstrap' +import { Row, Col } from 'react-bootstrap' import * as projectActions from '../actions/projects' -import { - getComponentMessages, - isModuleEnabled -} from '../../common/util/config' import type { ManagerUserState } from '../../types/reducers' import type { Project } from '../../types' -import DeploymentSettings from './deployment/DeploymentSettings' import ProjectSettingsForm from './ProjectSettingsForm' type Props = { - activeSettingsPanel?: ?string, deleteProject: typeof projectActions.deleteProject, project: Project, projectEditDisabled: boolean, @@ -25,66 +18,33 @@ type Props = { } export default class ProjectSettings extends Component { - messages = getComponentMessages('ProjectSettings') - _updateProjectSettings = (project: Project, settings: Object) => { - const {updateProject} = this.props + const { updateProject } = this.props // Update project and re-fetch feeds. updateProject(project.id, settings, true) } render () { const { - activeSettingsPanel, deleteProject, project, projectEditDisabled, updateProject, user } = this.props - const activePanel = !activeSettingsPanel - ? - : - return ( - - - - - - - {this.messages('title')} - - - {isModuleEnabled('deployment') - ? ( - - {this.messages('deployment.title')} - - ) - : null - } - - - - - {activePanel} - - - ) + return + + + + + } } diff --git a/lib/manager/components/ProjectViewer.js b/lib/manager/components/ProjectViewer.js index 62fb566f3..95705a820 100644 --- a/lib/manager/components/ProjectViewer.js +++ b/lib/manager/components/ProjectViewer.js @@ -138,7 +138,6 @@ export default class ProjectViewer extends Component { render () { const { activeComponent, - activeSubComponent, createFeedSource, isFetching, project, @@ -265,7 +264,6 @@ export default class ProjectViewer extends Component { // keyboard listener is not active while form is not visible. activeComponent === 'settings' && ( diff --git a/lib/manager/components/deployment/DeploymentSettings.js b/lib/manager/components/deployment/DeploymentSettings.js deleted file mode 100644 index 31faf4eb8..000000000 --- a/lib/manager/components/deployment/DeploymentSettings.js +++ /dev/null @@ -1,336 +0,0 @@ -// @flow - -import Icon from '@conveyal/woonerf/components/icon' -// $FlowFixMe coalesce method is missing in flow type -import {coalesce, get, set} from 'object-path' -import React, {Component} from 'react' -import { - Button, - Col, - ControlLabel, - FormControl, - FormGroup, - Glyphicon, - Panel, - Radio, - Row -} from 'react-bootstrap' -import update from 'react-addons-update' -import {shallowEqual} from 'react-pure-render' -import {withRouter} from 'react-router' - -import FormInput from '../../../common/components/FormInput' -import {getComponentMessages} from '../../../common/util/config' -import CollapsiblePanel from '../CollapsiblePanel' -import {parseBounds} from '../../util' -import {FIELDS, SERVER_FIELDS, UPDATER_FIELDS} from '../../util/deployment' -import {updateProject} from '../../actions/projects' -import type {Project} from '../../../types' - -type Props = { - editDisabled: boolean, - project: Project, - updateProject: typeof updateProject -} - -type State = { - buildConfig: Object, - routerConfig: Object, - useCustomOsmBounds?: boolean -} - -class DeploymentSettings extends Component { - messages = getComponentMessages('DeploymentSettings') - - state = { - buildConfig: get(this.props, 'project.buildConfig') || {}, - routerConfig: get(this.props, 'project.routerConfig') || {} - } - - componentWillReceiveProps (nextProps) { - if (nextProps.project.lastUpdated !== this.props.project.lastUpdated) { - // Reset state using project data if it is updated. - this.setState({ - buildConfig: get(nextProps, 'project.buildConfig') || {}, - routerConfig: get(nextProps, 'project.routerConfig') || {} - }) - } - } - - _clearBuildConfig = () => { - this.props.updateProject(this.props.project.id, {buildConfig: {}}) - } - - _clearRouterConfig = () => { - this.props.updateProject(this.props.project.id, {routerConfig: {}}) - } - - _getOnChange = (evt, index = null) => { - let item = FIELDS.find(f => f.name === evt.target.name) - if (!item) item = UPDATER_FIELDS.find(f => f.name === evt.target.name) - if (!item) item = SERVER_FIELDS.find(f => f.name === evt.target.name) - if (item) { - const stateUpdate = {} - item.effects && item.effects.forEach(e => { - set(stateUpdate, `${e.key}.$set`, e.value) - }) - switch (item.type) { - case 'checkbox': - return this._onChangeCheckbox(evt, stateUpdate, index) - case 'select-bool': - return this._onSelectBool(evt, stateUpdate, index) - case 'number': - return this._onChangeNumber(evt, stateUpdate, index) - default: - // check for split property, which indicates that comma-separated list should be split into array - if (item.split) { - return this._onChangeSplit(evt, stateUpdate, index) - } else { - return this._onChange(evt, stateUpdate, index) - } - } - } else { - console.warn('no onChange function available') - } - } - - _onChangeCheckbox = (evt, stateUpdate = {}, index = null) => { - const name = index !== null ? evt.target.name.replace('$index', `${index}`) : evt.target.name - set(stateUpdate, `${name}.$set`, evt.target.checked) - this.setState(update(this.state, stateUpdate)) - } - - _onChangeSplit = (evt, stateUpdate = {}, index = null) => { - const name = index !== null ? evt.target.name.replace('$index', `${index}`) : evt.target.name - set(stateUpdate, `${name}.$set`, evt.target.value.split(',')) - this.setState(update(this.state, stateUpdate)) - } - - _onAddUpdater = () => { - const stateUpdate = {} - set(stateUpdate, - `routerConfig.updaters.$${this.state.routerConfig.updaters ? 'push' : 'set'}`, - [{type: '', url: '', frequencySec: 30, sourceType: '', feedId: ''}] - ) - this.setState(update(this.state, stateUpdate)) - } - - _onRemoveUpdater = (index) => { - const stateUpdate = {} - set(stateUpdate, `routerConfig.updaters.$splice`, [[index, 1]]) - this.setState(update(this.state, stateUpdate)) - } - - _onChange = (evt, stateUpdate = {}, index = null) => { - const name = index !== null ? evt.target.name.replace('$index', `${index}`) : evt.target.name - // If value is empty string or undefined, set to null in settings object. - // Otherwise, certain fields (such as 'fares') would cause issues with OTP. - set(stateUpdate, `${name}.$set`, evt.target.value || null) - this.setState(update(this.state, stateUpdate)) - } - - _onChangeNumber = (evt, stateUpdate = {}, index = null) => { - const name = index !== null ? evt.target.name.replace('$index', `${index}`) : evt.target.name - set(stateUpdate, `${name}.$set`, +evt.target.value) - this.setState(update(this.state, stateUpdate)) - } - - _onSelectBool = (evt, stateUpdate = {}, index = null) => { - const name = index !== null ? evt.target.name.replace('$index', `${index}`) : evt.target.name - set(stateUpdate, `${name}.$set`, (evt.target.value === 'true')) - this.setState(update(this.state, stateUpdate)) - } - - _getFields = (fields, state, filter) => { - return fields - .filter(f => filter ? f.name.startsWith(filter) : f) - .map((f, index) => { - const shouldRender = () => { - // check for conditional render, e.g. elevationBucket is dependent on fetchElevationUS - if (f.condition) { - const {key, value} = f.condition - const val = get(state, `${key}`) - if (val !== value) return false - } - return true - } - return ( - - ) - }) - } - - _onSave = (evt) => this.props.updateProject(this.props.project.id, this.state, true) - - _onToggleCustomBounds = (evt) => { - const stateUpdate = { useCustomOsmBounds: { $set: (evt.target.value === 'true') } } - this.setState(update(this.state, stateUpdate)) - } - - _onChangeBounds = (evt) => { - const parsedBoundsResult = parseBounds(evt.target.value) - if (parsedBoundsResult.valid) { - this.setState( - update( - this.state, - { - $set: { - bounds: parsedBoundsResult.bounds - } - } - ) - ) - } - } - - /** - * Get value for key from state or, if undefined, default to project property - * from props. - */ - _getValue = (key) => coalesce(this.state, [key], this.props.project[key]) - - /** - * Determine if deployment settings have been modified by checking that every - * item in the state matches the original object found in the project object. - */ - _noEdits = () => Object.keys(this.state) - // $FlowFixMe it's fine if key doesn't exist in this.props.project - .every(key => shallowEqual(this.state[key], this.props.project[key])) - - render () { - const updaters = get(this.state, 'routerConfig.updaters') || [] - const {editDisabled, project} = this.props - return ( -
- {/* Build config settings */} - - - - {this.messages('buildConfig.title')} - - - {this._getFields(FIELDS, this.state, 'buildConfig')} - - - {/* Router config settings */} - - - - {' '} - {this.messages('routerConfig.title')} - - - {this._getFields(FIELDS, this.state, 'routerConfig')} - - - {/* Real-time Updaters (technically still a part of router config) */} - - - - {' '} - {this.messages('routerConfig.updaters.title')} - - - {updaters.map((u, i) => ( - - {u.type}{' '} - {u.url} - - : `[${this.messages('routerConfig.updaters.placeholder')}]` - } - /> - ))} - - - {/* OSM extract settings */} - - {this.messages('osm.title')} - - - - {this.messages('osm.gtfs')} - - - {this.messages('osm.custom')} - - - {project.useCustomOsmBounds || this.state.useCustomOsmBounds - ? - - - {this.messages('osm.bounds')} - - - - - : null - } - - - - - {/* Save button */} - - - -
- ) - } -} - -export default withRouter(DeploymentSettings)