From 703091949bae8a842aba193f4dcc2fe5ab5b43e0 Mon Sep 17 00:00:00 2001 From: Mark Friedrich Date: Fri, 16 Nov 2018 19:09:12 +0100 Subject: [PATCH] - moved ajax endpoints (connections, systems) into new REST API endpoints, #274 - improved error handling in case an Exception is thrown --- .../controller/api/{ => rest}/connection.php | 72 +++---- app/main/controller/api/rest/log.php | 1 - app/main/controller/api/rest/system.php | 190 +++++++++++++++++ app/main/controller/api/system.php | 165 -------------- app/main/controller/controller.php | 2 +- app/main/db/database.php | 2 +- app/main/exception/pathfinderexception.php | 8 +- app/main/exception/validationexception.php | 2 +- app/main/lib/config.php | 17 ++ app/main/model/systemmodel.php | 10 +- app/routes.ini | 3 +- js/app/init.js | 5 - js/app/map/map.js | 201 +++++------------- js/app/map/system.js | 134 ++++-------- js/app/map/util.js | 62 ++++++ js/app/ui/dialog/map_info.js | 2 +- js/app/ui/module/connection_info.js | 57 +---- js/app/ui/module/system_info.js | 65 ++---- js/app/util.js | 66 +++++- package.json | 2 +- public/js/v1.4.3/app/init.js | 5 - public/js/v1.4.3/app/map/map.js | 201 +++++------------- public/js/v1.4.3/app/map/system.js | 134 ++++-------- public/js/v1.4.3/app/map/util.js | 62 ++++++ public/js/v1.4.3/app/ui/dialog/map_info.js | 2 +- .../v1.4.3/app/ui/module/connection_info.js | 57 +---- public/js/v1.4.3/app/ui/module/system_info.js | 65 ++---- public/js/v1.4.3/app/util.js | 66 +++++- 28 files changed, 754 insertions(+), 904 deletions(-) rename app/main/controller/api/{ => rest}/connection.php (57%) create mode 100644 app/main/controller/api/rest/system.php diff --git a/app/main/controller/api/connection.php b/app/main/controller/api/rest/connection.php similarity index 57% rename from app/main/controller/api/connection.php rename to app/main/controller/api/rest/connection.php index 9c91f3b5..709f0d50 100644 --- a/app/main/controller/api/connection.php +++ b/app/main/controller/api/rest/connection.php @@ -1,51 +1,40 @@ this function is called for update * @param \Base $f3 * @throws \Exception + * @throws \ZMQSocketException */ - public function save(\Base $f3){ - $postData = (array)$f3->get('POST'); - - $return = (object) []; - $return->error = []; - $return->connectionData = (object) []; - - if( - isset($postData['connectionData']) && - isset($postData['mapData']) - ){ - $mapData = (array)$postData['mapData']; - $connectionData = (array)$postData['connectionData']; + public function put(\Base $f3){ + $requestData = $this->getRequestData($f3); + $connectionData = []; + if($mapId = (int)$requestData['mapId']){ $activeCharacter = $this->getCharacter(); - // get map model and check map access /** * @var Model\MapModel $map */ $map = Model\BasicModel::getNew('MapModel'); - $map->getById( (int)$mapData['id'] ); + $map->getById($mapId); - if( $map->hasAccess($activeCharacter) ){ - $source = $map->getSystemById( $connectionData['source'] ); - $target = $map->getSystemById( $connectionData['target'] ); + if($map->hasAccess($activeCharacter)){ + $source = $map->getSystemById((int)$requestData['source']); + $target = $map->getSystemById((int)$requestData['target']); if( !is_null($source) && @@ -55,7 +44,7 @@ class Connection extends Controller\AccessController { * @var $connection Model\ConnectionModel */ $connection = Model\BasicModel::getNew('ConnectionModel'); - $connection->getById( (int)$connectionData['id'] ); + $connection->getById((int)$requestData['id']); $connection->mapId = $map; $connection->source = $source; @@ -66,30 +55,30 @@ class Connection extends Controller\AccessController { $connection->setDefaultTypeData(); if($connection->save($activeCharacter)){ - $return->connectionData = $connection->getData(); + $connectionData = $connection->getData(); // broadcast map changes $this->broadcastMapData($connection->mapId); - }else{ - $return->error = $connection->getErrors(); } } } } - echo json_encode($return); + $this->out($connectionData); } /** - * delete connection * @param \Base $f3 + * @param $params * @throws \Exception + * @throws \ZMQSocketException */ - public function delete(\Base $f3){ - $mapId = (int)$f3->get('POST.mapId'); - $connectionIds = (array)$f3->get('POST.connectionIds'); + public function delete(\Base $f3, $params){ + $requestData = $this->getRequestData($f3); + $connectionIds = array_map('intval', explode(',', (string)$params['id'])); + $deletedConnectionIds = []; - if($mapId){ + if($mapId = (int)$requestData['mapId']){ $activeCharacter = $this->getCharacter(); /** @@ -100,20 +89,21 @@ class Connection extends Controller\AccessController { if($map->hasAccess($activeCharacter)){ foreach($connectionIds as $connectionId){ - if( $connection = $map->getConnectionById($connectionId) ){ + if($connection = $map->getConnectionById($connectionId)){ $connection->delete( $activeCharacter ); $connection->reset(); + $deletedConnectionIds[] = $connectionId; } } // broadcast map changes - $this->broadcastMapData($map); + if(count($deletedConnectionIds)){ + $this->broadcastMapData($map); + } } - } - echo json_encode([]); + $this->out($deletedConnectionIds); } - -} \ No newline at end of file +} \ No newline at end of file diff --git a/app/main/controller/api/rest/log.php b/app/main/controller/api/rest/log.php index ce679339..e019ff5b 100644 --- a/app/main/controller/api/rest/log.php +++ b/app/main/controller/api/rest/log.php @@ -60,7 +60,6 @@ class Log extends AbstractRestController { $this->out($connectionData); } - /** * update log data * @param \Base $f3 diff --git a/app/main/controller/api/rest/system.php b/app/main/controller/api/rest/system.php new file mode 100644 index 00000000..46694ec0 --- /dev/null +++ b/app/main/controller/api/rest/system.php @@ -0,0 +1,190 @@ +getRequestData($f3); + $systemData = []; + + if($mapId = (int)$requestData['mapId']){ + $activeCharacter = $this->getCharacter(); + + /** + * @var $map Model\MapModel + */ + $map = Model\BasicModel::getNew('MapModel'); + $map->getById($mapId); + if($map->hasAccess($activeCharacter)){ + $system = $map->getNewSystem($requestData['systemId']); + $systemData = $this->update($system, $requestData)->getData(); + } + } + + $this->out($systemData); + } + + /** + * update existing system + * @param \Base $f3 + * @param $params + * @throws \Exception + */ + public function patch(\Base $f3, $params){ + $requestData = $this->getRequestData($f3); + $systemData = []; + + if($systemId = (int)$params['id']){ + $activeCharacter = $this->getCharacter(); + + /** + * @var $system Model\SystemModel + */ + $system = Model\BasicModel::getNew('SystemModel'); + $system->getById($systemId); + + if($system->hasAccess($activeCharacter)){ + $systemData = $this->update($system, $requestData)->getData(); + } + } + + $this->out($systemData); + } + + /** + * @param \Base $f3 + * @param $params + * @throws \ZMQSocketException + * @throws \Exception + */ + public function delete(\Base $f3, $params){ + $requestData = $this->getRequestData($f3); + $systemIds = array_map('intval', explode(',', (string)$params['id'])); + $deletedSystemIds = []; + + if($mapId = (int)$requestData['mapId']){ + $activeCharacter = $this->getCharacter(); + + /** + * @var Model\MapModel $map + */ + $map = Model\BasicModel::getNew('MapModel'); + $map->getById($mapId); + + if($map->hasAccess($activeCharacter)){ + $newSystemModel = Model\BasicModel::getNew('SystemModel'); + foreach($systemIds as $systemId){ + if($system = $map->getSystemById($systemId)){ + // check whether system should be deleted OR set "inactive" + if($this->checkDeleteMode($map, $system)){ + // delete log + // -> first set updatedCharacterId -> required for activity log + $system->updatedCharacterId = $activeCharacter; + $system->update(); + + // ... now get fresh object and delete.. + $newSystemModel->getById($system->_id, 0); + $newSystemModel->erase(); + $newSystemModel->reset(); + }else{ + // keep data -> set "inactive" + $system->setActive(false); + $system->save($activeCharacter); + } + + $system->reset(); + + $deletedSystemIds[] = $systemId; + } + } + // broadcast map changes + if(count($deletedSystemIds)){ + $this->broadcastMapData($map); + } + } + } + + $this->out($deletedSystemIds); + } + + // ---------------------------------------------------------------------------------------------------------------- + + /** + * update system with new data + * @param Model\SystemModel $system + * @param array $systemData + * @return Model\SystemModel + * @throws \ZMQSocketException + * @throws \Exception + */ + private function update(Model\SystemModel $system, array $systemData) : Model\SystemModel { + $activeCharacter = $this->getCharacter(); + + // statusId === 0 is 'auto' status -> keep current status + // -> relevant systems that already have a status (inactive systems) + if( (int)$systemData['statusId'] <= 0 ){ + unset($systemData['statusId']); + } + + if( !$system->dry() ){ + // activate system (e.g. was inactive)) + $system->setActive(true); + } + + $system->setData($systemData); + $system->save($activeCharacter); + + // get data from "fresh" model (e.g. some relational data has changed: "statusId") + /** + * @var $newSystem Model\SystemModel + */ + $newSystem = Model\BasicModel::getNew('SystemModel'); + $newSystem->getById($system->_id, 0); + $newSystem->clearCacheData(); + + // broadcast map changes + $this->broadcastMapData($newSystem->mapId); + + return $newSystem; + } + + /** + * checks whether a system should be "deleted" or set "inactive" (keep some data) + * @param Model\MapModel $map + * @param Model\SystemModel $system + * @return bool + */ + private function checkDeleteMode(Model\MapModel $map, Model\SystemModel $system) : bool { + $delete = true; + + if( !empty($system->description) ){ + // never delete systems with custom description set! + $delete = false; + }elseif( + $map->persistentAliases && + !empty($system->alias) && + ($system->alias != $system->name) + ){ + // map setting "persistentAliases" is active (default) AND + // alias is set and != name + $delete = false; + } + + return $delete; + } + +} \ No newline at end of file diff --git a/app/main/controller/api/system.php b/app/main/controller/api/system.php index f7edf6ee..090828e5 100644 --- a/app/main/controller/api/system.php +++ b/app/main/controller/api/system.php @@ -10,7 +10,6 @@ namespace Controller\Api; use Controller; use Model; -use Exception; class System extends Controller\AccessController { @@ -26,88 +25,6 @@ class System extends Controller\AccessController { return sprintf(self::CACHE_KEY_GRAPH, 'SYSTEM_' . $systemId); } - /** - * save a new system to a a map - * @param \Base $f3 - * @throws \Exception - */ - public function save(\Base $f3){ - $postData = (array)$f3->get('POST'); - - $return = (object) []; - $return->error = []; - $return->systemData = (object) []; - - if( - isset($postData['systemData']) && - isset($postData['mapData']) - ){ - $activeCharacter = $this->getCharacter(); - $systemData = (array)$postData['systemData']; - $mapData = (array)$postData['mapData']; - $systemModel = null; - - if( (int)$systemData['statusId'] <= 0 ){ - unset($systemData['statusId']); - } - - if( isset($systemData['id']) ){ - // update existing system (e.g. set description) ------------------------------------------------------ - /** - * @var $system Model\SystemModel - */ - $system = Model\BasicModel::getNew('SystemModel'); - $system->getById($systemData['id']); - if( - !$system->dry() && - $system->hasAccess($activeCharacter) - ){ - // system model found - // activate system (e.g. was inactive)) - $system->setActive(true); - $systemModel = $system; - } - }elseif( isset($mapData['id']) ){ - // save NEW system ------------------------------------------------------------------------------------ - /** - * @var $map Model\MapModel - */ - $map = Model\BasicModel::getNew('MapModel'); - $map->getById($mapData['id']); - if($map->hasAccess($activeCharacter)){ - $systemModel = $map->getNewSystem($systemData['systemId']); - } - } - - if( !is_null($systemModel) ){ - try{ - // set/update system custom data - $systemModel->copyfrom($systemData, ['statusId', 'locked', 'rallyUpdated', 'position', 'description']); - - if($systemModel->save($activeCharacter)){ - // get data from "fresh" model (e.g. some relational data has changed: "statusId") - /** - * @var $newSystemModel Model\SystemModel - */ - $newSystemModel = Model\BasicModel::getNew('SystemModel'); - $newSystemModel->getById( $systemModel->_id, 0); - $newSystemModel->clearCacheData(); - $return->systemData = $newSystemModel->getData(); - - // broadcast map changes - $this->broadcastMapData($newSystemModel->mapId); - }else{ - $return->error = $systemModel->getErrors(); - } - }catch(Exception\ValidationException $e){ - $return->error[] = $e->getError(); - } - } - } - - echo json_encode($return); - } - /** * get system log data from CCP API import * system Kills, Jumps,.... @@ -294,87 +211,5 @@ class System extends Controller\AccessController { echo json_encode($return); } - /** - * delete systems and all its connections from map - * -> set "active" flag - * @param \Base $f3 - * @throws \Exception - */ - public function delete(\Base $f3){ - $mapId = (int)$f3->get('POST.mapId'); - $systemIds = array_map('intval', (array)$f3->get('POST.systemIds')); - - $return = (object) []; - $return->deletedSystemIds = []; - - if($mapId){ - $activeCharacter = $this->getCharacter(); - - /** - * @var Model\MapModel $map - */ - $map = Model\BasicModel::getNew('MapModel'); - $map->getById($mapId); - - if($map->hasAccess($activeCharacter)){ - $newSystemModel = Model\BasicModel::getNew('SystemModel'); - foreach($systemIds as $systemId){ - if( $system = $map->getSystemById($systemId) ){ - // check whether system should be deleted OR set "inactive" - if( $this->checkDeleteMode($map, $system) ){ - // delete log - // -> first set updatedCharacterId -> required for activity log - $system->updatedCharacterId = $activeCharacter; - $system->update(); - - // ... now get fresh object and delete.. - $newSystemModel->getById( $system->id, 0); - $newSystemModel->erase(); - $newSystemModel->reset(); - }else{ - // keep data -> set "inactive" - $system->setActive(false); - $system->save($activeCharacter); - } - - $system->reset(); - - $return->deletedSystemIds[] = $systemId; - } - } - // broadcast map changes - if(count($return->deletedSystemIds)){ - $this->broadcastMapData($map); - } - } - } - - echo json_encode($return); - } - - /** - * checks whether a system should be "deleted" or set "inactive" (keep some data) - * @param Model\MapModel $map - * @param Model\SystemModel $system - * @return bool - */ - protected function checkDeleteMode(Model\MapModel $map, Model\SystemModel $system){ - $delete = true; - - if( !empty($system->description) ){ - // never delete systems with custom description set! - $delete = false; - }elseif( - $map->persistentAliases && - !empty($system->alias) && - ($system->alias != $system->name) - ){ - // map setting "persistentAliases" is active (default) AND - // alias is set and != name - $delete = false; - } - - return $delete; - } } diff --git a/app/main/controller/controller.php b/app/main/controller/controller.php index 53ecdf41..065b0440 100644 --- a/app/main/controller/controller.php +++ b/app/main/controller/controller.php @@ -690,7 +690,7 @@ class Controller { $errorData['code'], $errorData['status'], $errorData['text'], - $f3->get('DEBUG') === 3 ? $errorData['trace'] : null + $f3->get('DEBUG') >= 1 ? $errorData['trace'] : null ); } diff --git a/app/main/db/database.php b/app/main/db/database.php index f5f09bcb..91d121f6 100644 --- a/app/main/db/database.php +++ b/app/main/db/database.php @@ -127,7 +127,7 @@ class Database extends \Prefab { ]; // set ERRMODE depending on pathfinders global DEBUG level - if($f3->get('DEBUG') >= 3){ + if($f3->get('DEBUG') >= 1){ $options[\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_WARNING; }else{ $options[\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_EXCEPTION; diff --git a/app/main/exception/pathfinderexception.php b/app/main/exception/pathfinderexception.php index 8fa45264..2636e1d4 100644 --- a/app/main/exception/pathfinderexception.php +++ b/app/main/exception/pathfinderexception.php @@ -8,6 +8,7 @@ namespace Exception; +use lib\Config; class PathfinderException extends \Exception { @@ -41,10 +42,11 @@ class PathfinderException extends \Exception { $error = (object) []; $error->type = 'error'; $error->code = $this->getResponseCode(); - $error->status = @constant('Base::HTTP_' . $this->getResponseCode()); + $error->status = Config::getHttpStatusByCode($this->getResponseCode()); $error->message = $this->getMessage(); - //$error->trace = $this->getTraceAsString(); - $error->trace = $this->getTrace(); + if(\Base::instance()->get('DEBUG') >= 1){ + $error->trace = preg_split('/\R/', $this->getTraceAsString()); // no $this->>getTrace() here -> to much data + } return $error; } diff --git a/app/main/exception/validationexception.php b/app/main/exception/validationexception.php index bf56c3b3..abfbf668 100644 --- a/app/main/exception/validationexception.php +++ b/app/main/exception/validationexception.php @@ -12,7 +12,7 @@ namespace Exception; class ValidationException extends PathfinderException { protected $codes = [ - 2000 => 593 + 2000 => 422 ]; /** diff --git a/app/main/lib/config.php b/app/main/lib/config.php index 66934f41..3a10c126 100644 --- a/app/main/lib/config.php +++ b/app/main/lib/config.php @@ -30,6 +30,9 @@ class Config extends \Prefab { */ const ARRAY_KEYS = ['CCP_ESI_SCOPES', 'CCP_ESI_SCOPES_ADMIN']; + const + HTTP_422='Unprocessable Entity'; + /** * all environment data * @var array @@ -392,4 +395,18 @@ class Config extends \Prefab { return $data; } + /** + * get HTTP status message by HTTP return code + * -> either from F3 or from self::Config constants + * @param int $code + * @return string + */ + static function getHttpStatusByCode(int $code) : string { + if(empty($status = @constant('Base::HTTP_' . $code))){ + $status = @constant('self::HTTP_' . $code); + } + + return $status; + } + } \ No newline at end of file diff --git a/app/main/model/systemmodel.php b/app/main/model/systemmodel.php index e2081b1a..3a1b63f1 100644 --- a/app/main/model/systemmodel.php +++ b/app/main/model/systemmodel.php @@ -119,6 +119,14 @@ class SystemModel extends AbstractMapTrackingModel { ] ]; + /** + * set map data by an associative array + * @param array $data + */ + public function setData(array $data){ + $this->copyfrom($data, ['statusId', 'locked', 'rallyUpdated', 'position', 'description']); + } + /** * get map data as object * @return \stdClass @@ -271,7 +279,7 @@ class SystemModel extends AbstractMapTrackingModel { $valid = true; if(mb_strlen($val) > 9000){ $valid = false; - $this->throwValidationException($key); + $this->throwValidationException($key, 'Validation failed: "' . $key . '" too long'); } return $valid; } diff --git a/app/routes.ini b/app/routes.ini index 032e679a..fe164ec8 100644 --- a/app/routes.ini +++ b/app/routes.ini @@ -13,11 +13,12 @@ GET @map: /map* [sync] = Controller\MapContro ; admin panel GET @admin: /admin* [sync] = Controller\Admin->dispatch -; ajax wildcard APIs (throttled) +; AJAX API wildcard endpoints (not cached, throttled) GET|POST /api/@controller/@action [ajax] = Controller\Api\@controller->@action, 0, 512 GET|POST /api/@controller/@action/@arg1 [ajax] = Controller\Api\@controller->@action, 0, 512 GET|POST /api/@controller/@action/@arg1/@arg2 [ajax] = Controller\Api\@controller->@action, 0, 512 [maps] +; REST API wildcard endpoints (not cached, throttled) /api/rest/@controller* [ajax] = Controller\Api\Rest\@controller, 0, 512 /api/rest/@controller/@id [ajax] = Controller\Api\Rest\@controller, 0, 512 \ No newline at end of file diff --git a/js/app/init.js b/js/app/init.js index dcb3a80f..8538d7cc 100644 --- a/js/app/init.js +++ b/js/app/init.js @@ -35,14 +35,9 @@ define(['jquery'], ($) => { getMapLogData: '/api/map/getLogData', // ajax URL - get logs data // system API getSystemData: '/api/system/getData', // ajax URL - get system data - saveSystem: '/api/system/save', // ajax URL - saves system to map - deleteSystem: '/api/system/delete', // ajax URL - delete system from map getSystemGraphData: '/api/system/graphData', // ajax URL - get all system graph data setDestination: '/api/system/setDestination', // ajax URL - set destination pokeRally: '/api/system/pokeRally', // ajax URL - send rally point pokes - // connection API - saveConnection: '/api/connection/save', // ajax URL - save new connection to map - deleteConnection: '/api/connection/delete', // ajax URL - delete connection from map // signature API saveSignatureData: '/api/signature/save', // ajax URL - save signature data for system deleteSignatureData: '/api/signature/delete', // ajax URL - delete signature data for system diff --git a/js/app/map/map.js b/js/app/map/map.js index 8fa47343..1ff86a97 100644 --- a/js/app/map/map.js +++ b/js/app/map/map.js @@ -559,7 +559,7 @@ define([ // confirm dialog bootbox.confirm('Is this connection really gone?', function(result){ if(result){ - $().deleteConnections([activeConnection]); + MapUtil.deleteConnections([activeConnection]); } }); break; @@ -1385,158 +1385,73 @@ define([ * @param connection */ let saveConnection = function(connection){ - if( connection instanceof jsPlumb.Connection ){ + if(connection instanceof jsPlumb.Connection){ let map = connection._jsPlumb.instance; - let mapContainer = $( map.getContainer() ); - + let mapContainer = $(map.getContainer()); let mapId = mapContainer.data('id'); + let connectionData = MapUtil.getDataByConnection(connection); + connectionData.mapId = mapId; - let requestData = { - mapData: { - id: mapId - }, - connectionData: connectionData - }; + Util.request('PUT', 'connection', [], connectionData, { + connection: connection, + map: map, + mapId: mapId, + oldConnectionData: connectionData + }).then( + payload => { + let newConnectionData = payload.data; - $.ajax({ - type: 'POST', - url: Init.path.saveConnection, - data: requestData, - dataType: 'json', - context: { - connection: connection, - map: map, - mapId: mapId, - oldConnectionData: connectionData - } - }).done(function(responseData){ - let newConnectionData = responseData.connectionData; + if( !$.isEmptyObject(newConnectionData) ){ + let updateCon = false; - if( !$.isEmptyObject(newConnectionData) ){ - let updateCon = false; - - if(this.oldConnectionData.id > 0){ - // connection exists (e.g. drag&drop new target system... (ids should never changed) - let connection = $().getConnectionById(this.mapId, this.oldConnectionData.id); - updateCon = true; - }else{ - // new connection, check if connectionId was already updated (webSocket push is faster than ajax callback) - let connection = $().getConnectionById(this.mapId, newConnectionData.id); - - if(connection){ - // connection already updated - this.map.detach(this.connection, {fireEvent: false}); - }else{ - // .. else update this connection - connection = this.connection; + if(payload.context.oldConnectionData.id > 0){ + // connection exists (e.g. drag&drop new target system... (ids should never changed) + let connection = $().getConnectionById(payload.context.mapId, payload.context.oldConnectionData.id); updateCon = true; + }else{ + // new connection, check if connectionId was already updated (webSocket push is faster than ajax callback) + let connection = $().getConnectionById(payload.context.mapId, newConnectionData.id); + + if(connection){ + // connection already updated + payload.context.map.detach(payload.context.connection, {fireEvent: false}); + }else{ + // .. else update this connection + connection = payload.context.connection; + updateCon = true; + } } + + if(updateCon){ + // update connection data e.g. "scope" has auto detected + connection = updateConnection(connection, payload.context.oldConnectionData, newConnectionData); + + // new/updated connection should be cached immediately! + updateConnectionCache(payload.context.mapId, connection); + } + + // connection scope + let scope = MapUtil.getScopeInfoForConnection(newConnectionData.scope, 'label'); + + let title = 'New connection established'; + if(payload.context.oldConnectionData.id > 0){ + title = 'Connection switched'; + } + + Util.showNotify({title: title, text: 'Scope: ' + scope, type: 'success'}); + }else{ + // some save errors + payload.context.map.detach(payload.context.connection, {fireEvent: false}); } - - if(updateCon){ - // update connection data e.g. "scope" has auto detected - connection = updateConnection(connection, this.oldConnectionData, newConnectionData); - - // new/updated connection should be cached immediately! - updateConnectionCache(this.mapId, connection); - } - - // connection scope - let scope = MapUtil.getScopeInfoForConnection(newConnectionData.scope, 'label'); - - let title = 'New connection established'; - if(this.oldConnectionData.id > 0){ - title = 'Connection switched'; - } - - Util.showNotify({title: title, text: 'Scope: ' + scope, type: 'success'}); - }else{ - // some save errors - this.map.detach(this.connection, {fireEvent: false}); + }, + payload => { + // remove this connection from map + payload.context.map.detach(payload.context.connection, {fireEvent: false}); + Util.handleAjaxErrorResponse(payload); } - - // show errors - if( - responseData.error && - responseData.error.length > 0 - ){ - for(let i = 0; i < responseData.error.length; i++){ - let error = responseData.error[i]; - Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type}); - } - } - }).fail(function(jqXHR, status, error){ - // remove this connection from map - this.map.detach(this.connection, {fireEvent: false}); - - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': saveConnection', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }); - } - }; - - /** - * delete a connection and all related data - * @param connections - * @param callback - */ - $.fn.deleteConnections = function(connections, callback){ - if(connections.length > 0){ - - // remove connections from map - let removeConnections = function(tempConnections){ - for(let i = 0; i < tempConnections.length; i++){ - // if a connection is manually (drag&drop) detached, the jsPlumb instance does not exist any more - // connection is already deleted! - if(tempConnections[i]._jsPlumb){ - tempConnections[i]._jsPlumb.instance.detach(tempConnections[i], {fireEvent: false}); - } - } - }; - - // prepare delete request - let map = connections[0]._jsPlumb.instance; - let mapContainer = $( map.getContainer() ); - - let connectionIds = []; - // connectionIds for delete request - for(let i = 0; i < connections.length; i++){ - let connectionId = connections[i].getParameter('connectionId'); - // drag&drop a new connection does not have an id yet, if connection is not established correct - if(connectionId !== undefined){ - connectionIds[i] = connections[i].getParameter('connectionId'); - } - } - - if(connectionIds.length > 0){ - let requestData = { - mapId: mapContainer.data('id'), - connectionIds: connectionIds - }; - - $.ajax({ - type: 'POST', - url: Init.path.deleteConnection, - data: requestData, - dataType: 'json', - context: connections - }).done(function(data){ - // remove connections from map - removeConnections(this); - - // optional callback - if(callback){ - callback(); - } - }).fail(function(jqXHR, status, error){ - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': deleteSystem', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }); - } + ); } }; @@ -2181,7 +2096,7 @@ define([ newJsPlumbInstance.bind('connectionDetached', function(info, e){ // a connection is manually (drag&drop) detached! otherwise this event should not be send! let connection = info.connection; - $().deleteConnections([connection]); + MapUtil.deleteConnections([connection]); }); newJsPlumbInstance.bind('checkDropAllowed', function(params){ diff --git a/js/app/map/system.js b/js/app/map/system.js index be248a87..48acf959 100644 --- a/js/app/map/system.js +++ b/js/app/map/system.js @@ -56,47 +56,6 @@ define([ '- DPS and Logistic ships needed' }; - - /** - * save a new system and add it to the map - * @param requestData - * @param context - * @param callback - */ - let saveSystem = (requestData, context, callback) => { - $.ajax({ - type: 'POST', - url: Init.path.saveSystem, - data: requestData, - dataType: 'json', - context: context - }).done(function(responseData){ - let newSystemData = responseData.systemData; - - if( !$.isEmptyObject(newSystemData) ){ - Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'}); - - callback(newSystemData); - } - - if( - responseData.error && - responseData.error.length > 0 - ){ - for(let i = 0; i < responseData.error.length; i++){ - let error = responseData.error[i]; - Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type}); - } - } - }).fail(function(jqXHR, status, error){ - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }).always(function(){ - this.systemDialog.find('.modal-content').hideLoadingAnimation(); - }); - }; - /** * open "new system" dialog and add the system to map * optional the new system is connected to a "sourceSystem" (if available) @@ -256,7 +215,7 @@ define([ // get form Values let form = this.find('form'); - let systemDialogData = $(form).getFormValues(); + let formData = $(form).getFormValues(); // validate form form.validator('validate'); @@ -288,27 +247,31 @@ define([ }; } - systemDialogData.position = newPosition; + formData.position = newPosition; + formData.mapId = mapId; // ---------------------------------------------------------------------------------------- - let requestData = { - systemData: systemDialogData, - mapData: { - id: mapId - } - }; - this.find('.modal-content').showLoadingAnimation(); - saveSystem(requestData, { - systemDialog: this - }, (newSystemData) => { - // success callback - callback(map, newSystemData, sourceSystem); + Util.request('PUT', 'system', [], formData, { + systemDialog: systemDialog, + formElement: form, + map: map, + sourceSystem: sourceSystem + }, context => { + // always do + context.systemDialog.find('.modal-content').hideLoadingAnimation(); + }).then( + payload => { + Util.showNotify({title: 'New system', text: payload.data.name, type: 'success'}); + + callback(payload.context.map, payload.data, payload.context.sourceSystem); + bootbox.hideAll(); + }, + Util.handleAjaxErrorResponse + ); - bootbox.hideAll(); - }); return false; } } @@ -695,36 +658,29 @@ define([ */ let deleteSystems = (map, systems = [], callback = (systems) => {}) => { let mapContainer = $( map.getContainer() ); + let systemIds = systems.map(system => $(system).data('id')); - $.ajax({ - type: 'POST', - url: Init.path.deleteSystem, - data: { - mapId: mapContainer.data('id'), - systemIds: systems.map( system => $(system).data('id') ) + Util.request('DELETE', 'system', systemIds, { + mapId: mapContainer.data('id') + }, { + map: map, + systems: systems + }).then( + payload => { + // check if all systems were deleted that should get deleted + let deletedSystems = payload.context.systems.filter( + function(system){ + return this.indexOf( $(system).data('id') ) !== -1; + }, payload.data + ); + + // remove systems from map + removeSystems(payload.context.map, deletedSystems); + + callback(deletedSystems); }, - dataType: 'json', - context: { - map: map, - systems: systems - } - }).done(function(data){ - // check if all systems were deleted that should get deleted - let deletedSystems = this.systems.filter( - function(system){ - return this.indexOf( $(system).data('id') ) !== -1; - }, data.deletedSystemIds - ); - - // remove systems from map - removeSystems(this.map, deletedSystems); - - callback(deletedSystems); - }).fail(function(jqXHR, status, error){ - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': deleteSystem', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }); + Util.handleAjaxErrorResponse + ); }; /** @@ -733,7 +689,7 @@ define([ * @param systems */ let removeSystems = (map, systems) => { - let removeSystemCallbak = deleteSystem => { + let removeSystemCallback = deleteSystem => { map.remove(deleteSystem); }; @@ -741,10 +697,10 @@ define([ system = $(system); // check if system is "active" - if( system.hasClass(config.systemActiveClass) ){ + if(system.hasClass(config.systemActiveClass)){ delete Init.currentSystemData; // get parent Tab Content and fire clear modules event - let tabContentElement = MapUtil.getTabContentElementByMapElement( system ); + let tabContentElement = MapUtil.getTabContentElementByMapElement(system); $(tabContentElement).trigger('pf:removeSystemModules'); } @@ -759,7 +715,7 @@ define([ // remove system system.velocity('transition.whirlOut', { duration: Init.animationSpeed.mapDeleteSystem, - complete: removeSystemCallbak + complete: removeSystemCallback }); } }; diff --git a/js/app/map/util.js b/js/app/map/util.js index 9db974d7..13f96227 100644 --- a/js/app/map/util.js +++ b/js/app/map/util.js @@ -350,6 +350,67 @@ define([ return data; }; + /** + * delete a connection and all related data + * @param connections + * @param callback + */ + let deleteConnections = (connections, callback) => { + if(connections.length > 0){ + + // remove connections from map + let removeConnections = connections => { + for(let connection of connections){ + connection._jsPlumb.instance.detach(connection, {fireEvent: false}); + } + }; + + // prepare delete request + let map = connections[0]._jsPlumb.instance; + let mapContainer = $(map.getContainer()); + + // connectionIds for delete request + let connectionIds = []; + for(let connection of connections){ + let connectionId = connection.getParameter('connectionId'); + // drag&drop a new connection does not have an id yet, if connection is not established correct + if(connectionId !== undefined){ + connectionIds.push(connectionId); + } + } + + if(connectionIds.length > 0){ + Util.request('DELETE', 'connection', connectionIds, { + mapId: mapContainer.data('id') + }, { + connections: connections + }).then( + payload => { + // check if all connections were deleted that should get deleted + let deletedConnections = payload.context.connections.filter( + function(connection){ + // if a connection is manually (drag&drop) detached, the jsPlumb instance does not exist any more + // connection is already deleted! + return ( + connection._jsPlumb && + this.indexOf( connection.getParameter('connectionId') ) !== -1 + ); + }, payload.data + ); + + // remove connections from map + removeConnections(deletedConnections); + + if(callback){ + callback(); + } + }, + Util.handleAjaxErrorResponse + ); + } + } + }; + /** * get connection related data from a connection * -> data requires a signature bind to that connection @@ -1682,6 +1743,7 @@ define([ setConnectionWHStatus: setConnectionWHStatus, getScopeInfoForConnection: getScopeInfoForConnection, getDataByConnections: getDataByConnections, + deleteConnections: deleteConnections, getConnectionDataFromSignatures: getConnectionDataFromSignatures, getEndpointOverlayContent: getEndpointOverlayContent, getTabContentElementByMapElement: getTabContentElementByMapElement, diff --git a/js/app/ui/dialog/map_info.js b/js/app/ui/dialog/map_info.js index 2a5b07eb..84fb338e 100644 --- a/js/app/ui/dialog/map_info.js +++ b/js/app/ui/dialog/map_info.js @@ -654,7 +654,7 @@ define([ // deleteSignatures(row); let connection = $().getConnectionById(mapData.config.id, rowData.id); - $().deleteConnections([connection], function(){ + MapUtil.deleteConnections([connection], () => { // callback function after ajax "delete" success // remove table row tempTableElement.DataTable().rows(deleteRowElement).remove().draw(); diff --git a/js/app/ui/module/connection_info.js b/js/app/ui/module/connection_info.js index da1e503f..35ecf609 100644 --- a/js/app/ui/module/connection_info.js +++ b/js/app/ui/module/connection_info.js @@ -907,7 +907,7 @@ define([ connectionElement.find('table').showLoadingAnimation(); - request('DELETE', 'log', rowData.id, {}, { + Util.request('DELETE', 'log', rowData.id, {}, { connectionElement: connectionElement }, requestAlways) .then( @@ -929,7 +929,7 @@ define([ active: 1 }; - request('PATCH', 'log', rowData.id, requestData, { + Util.request('PATCH', 'log', rowData.id, requestData, { connectionElement: connectionElement }, requestAlways) .then( @@ -1006,57 +1006,6 @@ define([ } }; - let request = (action, entity, ids = [], data = {}, context = {}, always = null) => { - - let requestExecutor = (resolve, reject) => { - let payload = { - action: 'request', - name: action.toLowerCase() + entity.charAt(0).toUpperCase() + entity.slice(1) - }; - - // build request url -------------------------------------------------------------------------------------- - let url = Init.path.api + '/' + entity; - - let path = ''; - if(isNaN(ids)){ - if(Array.isArray(ids)){ - path += '/' + ids.join(','); - } - }else{ - let id = parseInt(ids, 10); - path += id ? '/' + id : ''; - } - url += path; - - $.ajax({ - type: action, - url: url, - data: JSON.stringify(data), - contentType: 'application/json; charset=utf-8', - dataType: 'json', - context: context - }).done(function(response){ - payload.data = response; - payload.context = this; - resolve(payload); - }).fail(function(jqXHR, status, error){ - payload.data = { - jqXHR: jqXHR, - status: status, - error: error - }; - payload.context = this; - reject(payload); - }).always(function(){ - if(always){ - always(this); - } - }); - }; - - return new Promise(requestExecutor); - }; - /** * * @param context @@ -1129,7 +1078,7 @@ define([ let method = formData.id ? 'PATCH' : 'PUT'; - request(method, 'log', formData.id, formData, { + Util.request(method, 'log', formData.id, formData, { connectionElement: connectionElement, formElement: form }, requestAlways) diff --git a/js/app/ui/module/system_info.js b/js/app/ui/module/system_info.js index 81562a36..e5c84146 100644 --- a/js/app/ui/module/system_info.js +++ b/js/app/ui/module/system_info.js @@ -51,44 +51,6 @@ define([ // max character length for system description let maxDescriptionLength = 9000; - /** - * save system (description) - * @param requestData - * @param context - * @param callback - */ - let saveSystem = (requestData, context, callback) => { - context.descriptionArea.showLoadingAnimation(); - - $.ajax({ - type: 'POST', - url: Init.path.saveSystem, - data: requestData, - dataType: 'json', - context: context - }).done(function(responseData){ - let newSystemData = responseData.systemData; - - if( !$.isEmptyObject(newSystemData) ){ - callback(newSystemData); - } - - if( - responseData.error && - responseData.error.length > 0 - ){ - for(let error of responseData.error){ - Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type}); - } - } - }).fail(function(jqXHR, status, error){ - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'}); - }).always(function(){ - this.descriptionArea.hideLoadingAnimation(); - }); - }; - /** * update trigger function for this module * compare data and update module @@ -307,21 +269,22 @@ define([ if(validDescription){ // ... valid -> save() - saveSystem({ - mapData: { - id: mapId - }, - systemData: { - id: systemData.id, - description: description - } + descriptionArea.showLoadingAnimation(); + + Util.request('PATCH', 'system', systemData.id, { + description: description }, { descriptionArea: descriptionArea - }, (systemData) => { - // .. save callback - context.$note.summernote('destroy'); - updateModule(moduleElement, systemData); - }); + }, context => { + // always do + context.descriptionArea.hideLoadingAnimation(); + }).then( + payload => { + context.$note.summernote('destroy'); + updateModule(moduleElement, payload.data); + }, + Util.handleAjaxErrorResponse + ); } }else{ // ... no changes -> no save() diff --git a/js/app/util.js b/js/app/util.js index 2204de10..319c38b3 100644 --- a/js/app/util.js +++ b/js/app/util.js @@ -1527,6 +1527,69 @@ define([ }); }; + /** + * Request data from Server + * -> This function should be used (in future) for all Ajax and REST API calls + * -> works as a "wrapper" for jQueries ajax() method + * @param action + * @param entity + * @param ids + * @param data + * @param context + * @param always + * @returns {Promise} + */ + let request = (action, entity, ids = [], data = {}, context = {}, always = null) => { + + let requestExecutor = (resolve, reject) => { + let payload = { + action: 'request', + name: action.toLowerCase() + entity.charAt(0).toUpperCase() + entity.slice(1) + }; + + // build request url -------------------------------------------------------------------------------------- + let url = Init.path.api + '/' + entity; + + let path = ''; + if(isNaN(ids)){ + if(Array.isArray(ids)){ + path += '/' + ids.join(','); + } + }else{ + let id = parseInt(ids, 10); + path += id ? '/' + id : ''; + } + url += path; + + $.ajax({ + type: action, + url: url, + data: JSON.stringify(data), + contentType: 'application/json; charset=utf-8', + dataType: 'json', + context: context + }).done(function(response){ + payload.data = response; + payload.context = this; + resolve(payload); + }).fail(function(jqXHR, status, error){ + payload.data = { + jqXHR: jqXHR, + status: status, + error: error + }; + payload.context = this; + reject(payload); + }).always(function(){ + if(always){ + always(this); + } + }); + }; + + return new Promise(requestExecutor); + }; + /** * global ajax error handler -> handles .fail() requests * @param payload @@ -1543,7 +1606,7 @@ define([ if(response.error && response.error.length > 0){ // build error notification reason from errors - reason = response.error.map(error => error.status).join('\n'); + reason = response.error.map(error => error.message ? error.message : error.status).join('\n'); // check if errors might belong to a HTML form -> check "context" if(payload.context.formElement){ @@ -3037,6 +3100,7 @@ define([ stopTabBlink: stopTabBlink, getLogInfo: getLogInfo, ajaxSetup: ajaxSetup, + request: request, handleAjaxErrorResponse: handleAjaxErrorResponse, setSyncStatus: setSyncStatus, getSyncType: getSyncType, diff --git a/package.json b/package.json index 722bc3fc..67c09748 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "jshint": "^2.9.6", "jshint-stylish": "^2.x.x", "lodash.padend": "4.6.x", - "node-notifier": "5.2.x", + "node-notifier": "^5.3.0", "pretty-bytes": "^5.1.0", "promised-del": "1.0.x", "slash": "2.x.x", diff --git a/public/js/v1.4.3/app/init.js b/public/js/v1.4.3/app/init.js index dcb3a80f..8538d7cc 100644 --- a/public/js/v1.4.3/app/init.js +++ b/public/js/v1.4.3/app/init.js @@ -35,14 +35,9 @@ define(['jquery'], ($) => { getMapLogData: '/api/map/getLogData', // ajax URL - get logs data // system API getSystemData: '/api/system/getData', // ajax URL - get system data - saveSystem: '/api/system/save', // ajax URL - saves system to map - deleteSystem: '/api/system/delete', // ajax URL - delete system from map getSystemGraphData: '/api/system/graphData', // ajax URL - get all system graph data setDestination: '/api/system/setDestination', // ajax URL - set destination pokeRally: '/api/system/pokeRally', // ajax URL - send rally point pokes - // connection API - saveConnection: '/api/connection/save', // ajax URL - save new connection to map - deleteConnection: '/api/connection/delete', // ajax URL - delete connection from map // signature API saveSignatureData: '/api/signature/save', // ajax URL - save signature data for system deleteSignatureData: '/api/signature/delete', // ajax URL - delete signature data for system diff --git a/public/js/v1.4.3/app/map/map.js b/public/js/v1.4.3/app/map/map.js index 8fa47343..1ff86a97 100644 --- a/public/js/v1.4.3/app/map/map.js +++ b/public/js/v1.4.3/app/map/map.js @@ -559,7 +559,7 @@ define([ // confirm dialog bootbox.confirm('Is this connection really gone?', function(result){ if(result){ - $().deleteConnections([activeConnection]); + MapUtil.deleteConnections([activeConnection]); } }); break; @@ -1385,158 +1385,73 @@ define([ * @param connection */ let saveConnection = function(connection){ - if( connection instanceof jsPlumb.Connection ){ + if(connection instanceof jsPlumb.Connection){ let map = connection._jsPlumb.instance; - let mapContainer = $( map.getContainer() ); - + let mapContainer = $(map.getContainer()); let mapId = mapContainer.data('id'); + let connectionData = MapUtil.getDataByConnection(connection); + connectionData.mapId = mapId; - let requestData = { - mapData: { - id: mapId - }, - connectionData: connectionData - }; + Util.request('PUT', 'connection', [], connectionData, { + connection: connection, + map: map, + mapId: mapId, + oldConnectionData: connectionData + }).then( + payload => { + let newConnectionData = payload.data; - $.ajax({ - type: 'POST', - url: Init.path.saveConnection, - data: requestData, - dataType: 'json', - context: { - connection: connection, - map: map, - mapId: mapId, - oldConnectionData: connectionData - } - }).done(function(responseData){ - let newConnectionData = responseData.connectionData; + if( !$.isEmptyObject(newConnectionData) ){ + let updateCon = false; - if( !$.isEmptyObject(newConnectionData) ){ - let updateCon = false; - - if(this.oldConnectionData.id > 0){ - // connection exists (e.g. drag&drop new target system... (ids should never changed) - let connection = $().getConnectionById(this.mapId, this.oldConnectionData.id); - updateCon = true; - }else{ - // new connection, check if connectionId was already updated (webSocket push is faster than ajax callback) - let connection = $().getConnectionById(this.mapId, newConnectionData.id); - - if(connection){ - // connection already updated - this.map.detach(this.connection, {fireEvent: false}); - }else{ - // .. else update this connection - connection = this.connection; + if(payload.context.oldConnectionData.id > 0){ + // connection exists (e.g. drag&drop new target system... (ids should never changed) + let connection = $().getConnectionById(payload.context.mapId, payload.context.oldConnectionData.id); updateCon = true; + }else{ + // new connection, check if connectionId was already updated (webSocket push is faster than ajax callback) + let connection = $().getConnectionById(payload.context.mapId, newConnectionData.id); + + if(connection){ + // connection already updated + payload.context.map.detach(payload.context.connection, {fireEvent: false}); + }else{ + // .. else update this connection + connection = payload.context.connection; + updateCon = true; + } } + + if(updateCon){ + // update connection data e.g. "scope" has auto detected + connection = updateConnection(connection, payload.context.oldConnectionData, newConnectionData); + + // new/updated connection should be cached immediately! + updateConnectionCache(payload.context.mapId, connection); + } + + // connection scope + let scope = MapUtil.getScopeInfoForConnection(newConnectionData.scope, 'label'); + + let title = 'New connection established'; + if(payload.context.oldConnectionData.id > 0){ + title = 'Connection switched'; + } + + Util.showNotify({title: title, text: 'Scope: ' + scope, type: 'success'}); + }else{ + // some save errors + payload.context.map.detach(payload.context.connection, {fireEvent: false}); } - - if(updateCon){ - // update connection data e.g. "scope" has auto detected - connection = updateConnection(connection, this.oldConnectionData, newConnectionData); - - // new/updated connection should be cached immediately! - updateConnectionCache(this.mapId, connection); - } - - // connection scope - let scope = MapUtil.getScopeInfoForConnection(newConnectionData.scope, 'label'); - - let title = 'New connection established'; - if(this.oldConnectionData.id > 0){ - title = 'Connection switched'; - } - - Util.showNotify({title: title, text: 'Scope: ' + scope, type: 'success'}); - }else{ - // some save errors - this.map.detach(this.connection, {fireEvent: false}); + }, + payload => { + // remove this connection from map + payload.context.map.detach(payload.context.connection, {fireEvent: false}); + Util.handleAjaxErrorResponse(payload); } - - // show errors - if( - responseData.error && - responseData.error.length > 0 - ){ - for(let i = 0; i < responseData.error.length; i++){ - let error = responseData.error[i]; - Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type}); - } - } - }).fail(function(jqXHR, status, error){ - // remove this connection from map - this.map.detach(this.connection, {fireEvent: false}); - - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': saveConnection', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }); - } - }; - - /** - * delete a connection and all related data - * @param connections - * @param callback - */ - $.fn.deleteConnections = function(connections, callback){ - if(connections.length > 0){ - - // remove connections from map - let removeConnections = function(tempConnections){ - for(let i = 0; i < tempConnections.length; i++){ - // if a connection is manually (drag&drop) detached, the jsPlumb instance does not exist any more - // connection is already deleted! - if(tempConnections[i]._jsPlumb){ - tempConnections[i]._jsPlumb.instance.detach(tempConnections[i], {fireEvent: false}); - } - } - }; - - // prepare delete request - let map = connections[0]._jsPlumb.instance; - let mapContainer = $( map.getContainer() ); - - let connectionIds = []; - // connectionIds for delete request - for(let i = 0; i < connections.length; i++){ - let connectionId = connections[i].getParameter('connectionId'); - // drag&drop a new connection does not have an id yet, if connection is not established correct - if(connectionId !== undefined){ - connectionIds[i] = connections[i].getParameter('connectionId'); - } - } - - if(connectionIds.length > 0){ - let requestData = { - mapId: mapContainer.data('id'), - connectionIds: connectionIds - }; - - $.ajax({ - type: 'POST', - url: Init.path.deleteConnection, - data: requestData, - dataType: 'json', - context: connections - }).done(function(data){ - // remove connections from map - removeConnections(this); - - // optional callback - if(callback){ - callback(); - } - }).fail(function(jqXHR, status, error){ - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': deleteSystem', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }); - } + ); } }; @@ -2181,7 +2096,7 @@ define([ newJsPlumbInstance.bind('connectionDetached', function(info, e){ // a connection is manually (drag&drop) detached! otherwise this event should not be send! let connection = info.connection; - $().deleteConnections([connection]); + MapUtil.deleteConnections([connection]); }); newJsPlumbInstance.bind('checkDropAllowed', function(params){ diff --git a/public/js/v1.4.3/app/map/system.js b/public/js/v1.4.3/app/map/system.js index be248a87..48acf959 100644 --- a/public/js/v1.4.3/app/map/system.js +++ b/public/js/v1.4.3/app/map/system.js @@ -56,47 +56,6 @@ define([ '- DPS and Logistic ships needed' }; - - /** - * save a new system and add it to the map - * @param requestData - * @param context - * @param callback - */ - let saveSystem = (requestData, context, callback) => { - $.ajax({ - type: 'POST', - url: Init.path.saveSystem, - data: requestData, - dataType: 'json', - context: context - }).done(function(responseData){ - let newSystemData = responseData.systemData; - - if( !$.isEmptyObject(newSystemData) ){ - Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'}); - - callback(newSystemData); - } - - if( - responseData.error && - responseData.error.length > 0 - ){ - for(let i = 0; i < responseData.error.length; i++){ - let error = responseData.error[i]; - Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type}); - } - } - }).fail(function(jqXHR, status, error){ - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }).always(function(){ - this.systemDialog.find('.modal-content').hideLoadingAnimation(); - }); - }; - /** * open "new system" dialog and add the system to map * optional the new system is connected to a "sourceSystem" (if available) @@ -256,7 +215,7 @@ define([ // get form Values let form = this.find('form'); - let systemDialogData = $(form).getFormValues(); + let formData = $(form).getFormValues(); // validate form form.validator('validate'); @@ -288,27 +247,31 @@ define([ }; } - systemDialogData.position = newPosition; + formData.position = newPosition; + formData.mapId = mapId; // ---------------------------------------------------------------------------------------- - let requestData = { - systemData: systemDialogData, - mapData: { - id: mapId - } - }; - this.find('.modal-content').showLoadingAnimation(); - saveSystem(requestData, { - systemDialog: this - }, (newSystemData) => { - // success callback - callback(map, newSystemData, sourceSystem); + Util.request('PUT', 'system', [], formData, { + systemDialog: systemDialog, + formElement: form, + map: map, + sourceSystem: sourceSystem + }, context => { + // always do + context.systemDialog.find('.modal-content').hideLoadingAnimation(); + }).then( + payload => { + Util.showNotify({title: 'New system', text: payload.data.name, type: 'success'}); + + callback(payload.context.map, payload.data, payload.context.sourceSystem); + bootbox.hideAll(); + }, + Util.handleAjaxErrorResponse + ); - bootbox.hideAll(); - }); return false; } } @@ -695,36 +658,29 @@ define([ */ let deleteSystems = (map, systems = [], callback = (systems) => {}) => { let mapContainer = $( map.getContainer() ); + let systemIds = systems.map(system => $(system).data('id')); - $.ajax({ - type: 'POST', - url: Init.path.deleteSystem, - data: { - mapId: mapContainer.data('id'), - systemIds: systems.map( system => $(system).data('id') ) + Util.request('DELETE', 'system', systemIds, { + mapId: mapContainer.data('id') + }, { + map: map, + systems: systems + }).then( + payload => { + // check if all systems were deleted that should get deleted + let deletedSystems = payload.context.systems.filter( + function(system){ + return this.indexOf( $(system).data('id') ) !== -1; + }, payload.data + ); + + // remove systems from map + removeSystems(payload.context.map, deletedSystems); + + callback(deletedSystems); }, - dataType: 'json', - context: { - map: map, - systems: systems - } - }).done(function(data){ - // check if all systems were deleted that should get deleted - let deletedSystems = this.systems.filter( - function(system){ - return this.indexOf( $(system).data('id') ) !== -1; - }, data.deletedSystemIds - ); - - // remove systems from map - removeSystems(this.map, deletedSystems); - - callback(deletedSystems); - }).fail(function(jqXHR, status, error){ - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': deleteSystem', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }); + Util.handleAjaxErrorResponse + ); }; /** @@ -733,7 +689,7 @@ define([ * @param systems */ let removeSystems = (map, systems) => { - let removeSystemCallbak = deleteSystem => { + let removeSystemCallback = deleteSystem => { map.remove(deleteSystem); }; @@ -741,10 +697,10 @@ define([ system = $(system); // check if system is "active" - if( system.hasClass(config.systemActiveClass) ){ + if(system.hasClass(config.systemActiveClass)){ delete Init.currentSystemData; // get parent Tab Content and fire clear modules event - let tabContentElement = MapUtil.getTabContentElementByMapElement( system ); + let tabContentElement = MapUtil.getTabContentElementByMapElement(system); $(tabContentElement).trigger('pf:removeSystemModules'); } @@ -759,7 +715,7 @@ define([ // remove system system.velocity('transition.whirlOut', { duration: Init.animationSpeed.mapDeleteSystem, - complete: removeSystemCallbak + complete: removeSystemCallback }); } }; diff --git a/public/js/v1.4.3/app/map/util.js b/public/js/v1.4.3/app/map/util.js index 9db974d7..13f96227 100644 --- a/public/js/v1.4.3/app/map/util.js +++ b/public/js/v1.4.3/app/map/util.js @@ -350,6 +350,67 @@ define([ return data; }; + /** + * delete a connection and all related data + * @param connections + * @param callback + */ + let deleteConnections = (connections, callback) => { + if(connections.length > 0){ + + // remove connections from map + let removeConnections = connections => { + for(let connection of connections){ + connection._jsPlumb.instance.detach(connection, {fireEvent: false}); + } + }; + + // prepare delete request + let map = connections[0]._jsPlumb.instance; + let mapContainer = $(map.getContainer()); + + // connectionIds for delete request + let connectionIds = []; + for(let connection of connections){ + let connectionId = connection.getParameter('connectionId'); + // drag&drop a new connection does not have an id yet, if connection is not established correct + if(connectionId !== undefined){ + connectionIds.push(connectionId); + } + } + + if(connectionIds.length > 0){ + Util.request('DELETE', 'connection', connectionIds, { + mapId: mapContainer.data('id') + }, { + connections: connections + }).then( + payload => { + // check if all connections were deleted that should get deleted + let deletedConnections = payload.context.connections.filter( + function(connection){ + // if a connection is manually (drag&drop) detached, the jsPlumb instance does not exist any more + // connection is already deleted! + return ( + connection._jsPlumb && + this.indexOf( connection.getParameter('connectionId') ) !== -1 + ); + }, payload.data + ); + + // remove connections from map + removeConnections(deletedConnections); + + if(callback){ + callback(); + } + }, + Util.handleAjaxErrorResponse + ); + } + } + }; + /** * get connection related data from a connection * -> data requires a signature bind to that connection @@ -1682,6 +1743,7 @@ define([ setConnectionWHStatus: setConnectionWHStatus, getScopeInfoForConnection: getScopeInfoForConnection, getDataByConnections: getDataByConnections, + deleteConnections: deleteConnections, getConnectionDataFromSignatures: getConnectionDataFromSignatures, getEndpointOverlayContent: getEndpointOverlayContent, getTabContentElementByMapElement: getTabContentElementByMapElement, diff --git a/public/js/v1.4.3/app/ui/dialog/map_info.js b/public/js/v1.4.3/app/ui/dialog/map_info.js index 2a5b07eb..84fb338e 100644 --- a/public/js/v1.4.3/app/ui/dialog/map_info.js +++ b/public/js/v1.4.3/app/ui/dialog/map_info.js @@ -654,7 +654,7 @@ define([ // deleteSignatures(row); let connection = $().getConnectionById(mapData.config.id, rowData.id); - $().deleteConnections([connection], function(){ + MapUtil.deleteConnections([connection], () => { // callback function after ajax "delete" success // remove table row tempTableElement.DataTable().rows(deleteRowElement).remove().draw(); diff --git a/public/js/v1.4.3/app/ui/module/connection_info.js b/public/js/v1.4.3/app/ui/module/connection_info.js index da1e503f..35ecf609 100644 --- a/public/js/v1.4.3/app/ui/module/connection_info.js +++ b/public/js/v1.4.3/app/ui/module/connection_info.js @@ -907,7 +907,7 @@ define([ connectionElement.find('table').showLoadingAnimation(); - request('DELETE', 'log', rowData.id, {}, { + Util.request('DELETE', 'log', rowData.id, {}, { connectionElement: connectionElement }, requestAlways) .then( @@ -929,7 +929,7 @@ define([ active: 1 }; - request('PATCH', 'log', rowData.id, requestData, { + Util.request('PATCH', 'log', rowData.id, requestData, { connectionElement: connectionElement }, requestAlways) .then( @@ -1006,57 +1006,6 @@ define([ } }; - let request = (action, entity, ids = [], data = {}, context = {}, always = null) => { - - let requestExecutor = (resolve, reject) => { - let payload = { - action: 'request', - name: action.toLowerCase() + entity.charAt(0).toUpperCase() + entity.slice(1) - }; - - // build request url -------------------------------------------------------------------------------------- - let url = Init.path.api + '/' + entity; - - let path = ''; - if(isNaN(ids)){ - if(Array.isArray(ids)){ - path += '/' + ids.join(','); - } - }else{ - let id = parseInt(ids, 10); - path += id ? '/' + id : ''; - } - url += path; - - $.ajax({ - type: action, - url: url, - data: JSON.stringify(data), - contentType: 'application/json; charset=utf-8', - dataType: 'json', - context: context - }).done(function(response){ - payload.data = response; - payload.context = this; - resolve(payload); - }).fail(function(jqXHR, status, error){ - payload.data = { - jqXHR: jqXHR, - status: status, - error: error - }; - payload.context = this; - reject(payload); - }).always(function(){ - if(always){ - always(this); - } - }); - }; - - return new Promise(requestExecutor); - }; - /** * * @param context @@ -1129,7 +1078,7 @@ define([ let method = formData.id ? 'PATCH' : 'PUT'; - request(method, 'log', formData.id, formData, { + Util.request(method, 'log', formData.id, formData, { connectionElement: connectionElement, formElement: form }, requestAlways) diff --git a/public/js/v1.4.3/app/ui/module/system_info.js b/public/js/v1.4.3/app/ui/module/system_info.js index 81562a36..e5c84146 100644 --- a/public/js/v1.4.3/app/ui/module/system_info.js +++ b/public/js/v1.4.3/app/ui/module/system_info.js @@ -51,44 +51,6 @@ define([ // max character length for system description let maxDescriptionLength = 9000; - /** - * save system (description) - * @param requestData - * @param context - * @param callback - */ - let saveSystem = (requestData, context, callback) => { - context.descriptionArea.showLoadingAnimation(); - - $.ajax({ - type: 'POST', - url: Init.path.saveSystem, - data: requestData, - dataType: 'json', - context: context - }).done(function(responseData){ - let newSystemData = responseData.systemData; - - if( !$.isEmptyObject(newSystemData) ){ - callback(newSystemData); - } - - if( - responseData.error && - responseData.error.length > 0 - ){ - for(let error of responseData.error){ - Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type}); - } - } - }).fail(function(jqXHR, status, error){ - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'}); - }).always(function(){ - this.descriptionArea.hideLoadingAnimation(); - }); - }; - /** * update trigger function for this module * compare data and update module @@ -307,21 +269,22 @@ define([ if(validDescription){ // ... valid -> save() - saveSystem({ - mapData: { - id: mapId - }, - systemData: { - id: systemData.id, - description: description - } + descriptionArea.showLoadingAnimation(); + + Util.request('PATCH', 'system', systemData.id, { + description: description }, { descriptionArea: descriptionArea - }, (systemData) => { - // .. save callback - context.$note.summernote('destroy'); - updateModule(moduleElement, systemData); - }); + }, context => { + // always do + context.descriptionArea.hideLoadingAnimation(); + }).then( + payload => { + context.$note.summernote('destroy'); + updateModule(moduleElement, payload.data); + }, + Util.handleAjaxErrorResponse + ); } }else{ // ... no changes -> no save() diff --git a/public/js/v1.4.3/app/util.js b/public/js/v1.4.3/app/util.js index 2204de10..319c38b3 100644 --- a/public/js/v1.4.3/app/util.js +++ b/public/js/v1.4.3/app/util.js @@ -1527,6 +1527,69 @@ define([ }); }; + /** + * Request data from Server + * -> This function should be used (in future) for all Ajax and REST API calls + * -> works as a "wrapper" for jQueries ajax() method + * @param action + * @param entity + * @param ids + * @param data + * @param context + * @param always + * @returns {Promise} + */ + let request = (action, entity, ids = [], data = {}, context = {}, always = null) => { + + let requestExecutor = (resolve, reject) => { + let payload = { + action: 'request', + name: action.toLowerCase() + entity.charAt(0).toUpperCase() + entity.slice(1) + }; + + // build request url -------------------------------------------------------------------------------------- + let url = Init.path.api + '/' + entity; + + let path = ''; + if(isNaN(ids)){ + if(Array.isArray(ids)){ + path += '/' + ids.join(','); + } + }else{ + let id = parseInt(ids, 10); + path += id ? '/' + id : ''; + } + url += path; + + $.ajax({ + type: action, + url: url, + data: JSON.stringify(data), + contentType: 'application/json; charset=utf-8', + dataType: 'json', + context: context + }).done(function(response){ + payload.data = response; + payload.context = this; + resolve(payload); + }).fail(function(jqXHR, status, error){ + payload.data = { + jqXHR: jqXHR, + status: status, + error: error + }; + payload.context = this; + reject(payload); + }).always(function(){ + if(always){ + always(this); + } + }); + }; + + return new Promise(requestExecutor); + }; + /** * global ajax error handler -> handles .fail() requests * @param payload @@ -1543,7 +1606,7 @@ define([ if(response.error && response.error.length > 0){ // build error notification reason from errors - reason = response.error.map(error => error.status).join('\n'); + reason = response.error.map(error => error.message ? error.message : error.status).join('\n'); // check if errors might belong to a HTML form -> check "context" if(payload.context.formElement){ @@ -3037,6 +3100,7 @@ define([ stopTabBlink: stopTabBlink, getLogInfo: getLogInfo, ajaxSetup: ajaxSetup, + request: request, handleAjaxErrorResponse: handleAjaxErrorResponse, setSyncStatus: setSyncStatus, getSyncType: getSyncType,