feat(ui): validation for api definitions & user-friendly error modal
MODIFY
manager/ui/war/plugins/api-manager/css/apiman.css
manager/ui/war/plugins/api-manager/css/apiman.css
@@ -1092,7 +1092,7 @@ ol.breadcrumb {
}
.error-page {
- margin-top: 30px;
+ margin-top: 5%;
}
.error-page .title {
font-size: 18px;
@@ -1245,11 +1245,13 @@ div.admin-page .admin-content .apiman-summaryrow .emphasis {
border-bottom: 1px solid rgb(238, 238, 238);
}
-.modal-header .modal-danger
+.modal-danger
{
background-color: #B42000;
+ color: white;
}
+
.modal-body {
padding: 25px;
}

MODIFY
manager/ui/war/plugins/api-manager/html/api/api-def.html
manager/ui/war/plugins/api-manager/html/api/api-def.html
name="definitionUrl"
class="apiman-form-control form-control"
type="text"
+ ng-disabled="selectedDefinitionType.value == 'None'"
>
<span class="input-group-btn">
<div apiman-permission="apiEdit"
<button type="button" class="btn btn-default"
ng-click="updateDefinitionFromUrl()"
style="margin-left: 5px"
- ng-disabled="updatedApiDefinitionUrl == null"
+ ng-disabled="updatedApiDefinitionUrl == null || selectedDefinitionType.value == 'None'"
apiman-i18n-key="load-definition">Load Definition
</button>
</div>
ng-blur="blur=true;focus=false;"
data-field="data"
class="apiman-form-control form-control apiman-form-data top-spacer"
- ng-disabled="isEntityDisabled()">
+ ng-disabled="isEntityDisabled() || selectedDefinitionType.value == 'None'">
</textarea>
</div>
<button type="button" class="btn btn-default" ng-mousedown="downloadDefinition()"
ng-disabled="isDirty || !apiDefinition" apiman-i18n-key="download">Download
</button>
- <button ng-disabled="!isDirty || updatedApiDefinition.length == 0"
+ <button ng-disabled="!isDirty || updatedApiDefinition.length == 0 || selectedDefinitionType.value == 'None'"
ng-mousedown="saveApi()"
apiman-action-btn=""
class="btn btn-primary"
</div>
<!-- /Content -->
</div>
+ <div ng-include="'plugins/api-manager/html/modals/errorModal.html'"></div>
</div> <!-- /container -->
</div>
</body>

ADD
manager/ui/war/plugins/api-manager/html/modals/errorModal.html
+<!-- Error Modal -->
+<script id="errorModal.html" type="text/ng-template">
+ <div class="modal-header modal-danger">
+ <h3 class="modal-title">{{ title }}</h3>
+ </div>
+ <div class="row modal-body">
+ <div class="col-sm-2">
+ <i class="fa fa-warning" style="font-size:48px;color:red"></i>
+ </div>
+ <div class="col-sm-10">
+ {{ message }}
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button class="btn btn-danger"
+ type="button"
+ ng-click="ok()"
+ apiman-i18n-key="ok">OK
+ </button>
+ </div>
+</script>
\ No newline at end of file

MODIFY
manager/ui/war/plugins/api-manager/ts/api/api-def.ts
manager/ui/war/plugins/api-manager/ts/api/api-def.ts
module Apiman {
export var ApiDefController = _module.controller('Apiman.ApiDefController',
- ['$q', '$rootScope', '$scope', '$location', 'PageLifecycle', 'ApiEntityLoader', 'OrgSvcs', 'Logger', '$routeParams', 'ApiDefinitionSvcs', 'Configuration', 'EntityStatusSvc', 'CurrentUser',
- ($q, $rootScope, $scope, $location, PageLifecycle, ApiEntityLoader, OrgSvcs, Logger, $routeParams, ApiDefinitionSvcs, Configuration, EntityStatusSvc, CurrentUser) => {
+ ['$q', '$rootScope', '$scope', '$location', 'Modals', 'PageLifecycle', 'ApiEntityLoader', 'OrgSvcs', 'Logger', '$routeParams', 'ApiDefinitionSvcs', 'Configuration', 'EntityStatusSvc', 'CurrentUser',
+ ($q, $rootScope, $scope, $location, Modals, PageLifecycle, ApiEntityLoader, OrgSvcs, Logger, $routeParams, ApiDefinitionSvcs, Configuration, EntityStatusSvc, CurrentUser) => {
var params = $routeParams;
$scope.organizationId = params.org;
@@ -36,10 +36,37 @@ module Apiman {
EntityStatusSvc.getEntity().modifiedBy = CurrentUser.getCurrentUser();
};
+ $scope.isValidDefinition = (data, definitionType) => {
+ switch (definitionType) {
+ case 'SwaggerJSON':
+ try {
+ $.parseJSON(data);
+ return true;
+ } catch (error) {
+ return false;
+ }
+ case 'WSDL':
+ try {
+ $.parseXML(data);
+ return true;
+ } catch (error) {
+ return false;
+ }
+ default:
+ return true;
+ }
+ }
+
$scope.saveApi = function () {
console.log('$scope.selectedDefinitionType.value: ' + $scope.selectedDefinitionType.value);
$scope.saveButton.state = 'in-progress';
+ if (!$scope.isValidDefinition($scope.updatedApiDefinition, $scope.selectedDefinitionType.value)) {
+ Modals.error('Invalid API Definition!','The specified API definition is not a valid ' + $scope.selectedDefinitionType.value, null);
+ $scope.saveButton.state = 'error';
+ return;
+ }
+
ApiDefinitionSvcs.updateApiDefinition(params.org, params.api, params.version,
$scope.updatedApiDefinition, $scope.selectedDefinitionType.value,
function() {
@@ -48,6 +75,7 @@ module Apiman {
$scope.finishModification();
},
function (error) {
+ Modals.rpcerror(error, null, null);
Logger.error("Error updating definition: {0}", error);
$scope.saveButton.state = 'error';
});
@@ -83,6 +111,7 @@ module Apiman {
$scope.updatedApiDefinition = definition;
},
function (error) {
+ Modals.rpcerror(error, null, null);
Logger.error("Error loading definition: {0}", error);
});
};
@@ -157,6 +186,7 @@ module Apiman {
$scope.finishModification();
},
function (error) {
+ Modals.rpcerror(error, null, null);
Logger.error("Error updating definition: {0}", error);
$scope.saveButton.state = 'error';
})

MODIFY
manager/ui/war/plugins/api-manager/ts/modals.ts
manager/ui/war/plugins/api-manager/ts/modals.ts
@@ -50,6 +50,73 @@ module ApimanModals {
});
modalInstance.result.then(yesCallback, noCallback);
+ },
+ // A standard error dialog
+ /////////////////////////////////
+ error: function (title, message, okCallBack) {
+ var options = {
+ title: title,
+ message: message
+ };
+
+ var modalInstance = $uibModal.open({
+ animation: true,
+ templateUrl: 'errorModal.html',
+ controller: 'ModalErrorCtrl',
+ resolve: {
+ options: function () {
+ return options;
+ }
+ }
+ });
+
+ modalInstance.result.then(okCallBack);
+ },
+ rpcerror: function (rpcdata, title, okCallBack) {
+ let message = "An unspecified error occurred when calling the Manager API";
+
+ if (title == null) {
+ title = "We ran into an error!"
+ }
+
+ try {
+ switch (typeof(rpcdata)) {
+ case "string":
+ message = JSON.parse(rpcdata).message;
+ break;
+
+ case "object":
+ message = rpcdata.message;
+ break;
+
+ default:
+ alert(typeof(rpcdata))
+ break;
+ }
+
+ } catch(error) {
+ console.error(error)
+ }
+
+ const options = {
+ title: title,
+ message: message
+
+ };
+
+ const modalInstance = $uibModal.open({
+ animation: true,
+ templateUrl: 'errorModal.html',
+ controller: 'ModalErrorCtrl',
+ resolve: {
+ options: function () {
+ return options;
+ }
+ }
+ });
+
+ modalInstance.result.then(okCallBack);
+
}
}
}]);
@@ -255,4 +322,19 @@ module ApimanModals {
]
);
+ export var ModalErrorCtrl = _module.controller('ModalErrorCtrl',
+ ['$scope', '$uibModalInstance', 'Logger', 'options',
+ ($scope, $uibModalInstance, Logger, options) => {
+
+ $scope.options = options;
+ $scope.title = $scope.options.title;
+ $scope.message = $scope.options.message;
+
+ $scope.ok = function () {
+ $uibModalInstance.close();
+ };
+ }
+ ]
+ );
+
}
\ No newline at end of file

feat(ui): validation for api definitions & user-friendly error modal

Co-authored-by: Estevao Souza <estevao_souza@lslgr.com.br>