feat(ui): contract approvals flow
MODIFY
manager/api/core/src/main/java/io/apiman/manager/api/core/IClientValidator.java
manager/api/core/src/main/java/io/apiman/manager/api/core/IClientValidator.java
*/
package io.apiman.manager.api.core;
+import io.apiman.manager.api.beans.clients.ClientStatus;
import io.apiman.manager.api.beans.clients.ClientVersionBean;
+import io.apiman.manager.api.beans.contracts.ContractBean;
+
+import java.util.List;
/**
* Validates the state of clients and client versions.
@@ -65,4 +69,5 @@ public interface IClientValidator {
*/
boolean isReady(ClientVersionBean client, boolean hasContracts) throws Exception;
+ ClientStatus determineStatus(ClientVersionBean bean, List<ContractBean> contracts);
}

MODIFY
manager/api/core/src/main/java/io/apiman/manager/api/core/util/EntityValidator.java
manager/api/core/src/main/java/io/apiman/manager/api/core/util/EntityValidator.java
@@ -19,7 +19,10 @@ import io.apiman.manager.api.beans.apis.ApiStatus;
import io.apiman.manager.api.beans.apis.ApiVersionBean;
import io.apiman.manager.api.beans.apis.ApiVersionStatusBean;
import io.apiman.manager.api.beans.apis.StatusItemBean;
+import io.apiman.manager.api.beans.clients.ClientStatus;
import io.apiman.manager.api.beans.clients.ClientVersionBean;
+import io.apiman.manager.api.beans.contracts.ContractBean;
+import io.apiman.manager.api.beans.contracts.ContractStatus;
import io.apiman.manager.api.beans.summary.ContractSummaryBean;
import io.apiman.manager.api.beans.summary.PolicySummaryBean;
import io.apiman.manager.api.core.IApiValidator;
@@ -28,10 +31,11 @@ import io.apiman.manager.api.core.IStorageQuery;
import io.apiman.manager.api.core.i18n.Messages;
import java.util.List;
-
import javax.inject.Inject;
/**
+ * TODO could rework this to return or set a status?
+ *
* Validates the state of various entities, including APIs and clients.
*
* @author eric.wittmann@redhat.com
@@ -52,23 +56,34 @@ public class EntityValidator implements IApiValidator, IClientValidator {
*/
@Override
public boolean isReady(ClientVersionBean client) throws Exception {
- boolean hasContracts = true;
-
List<ContractSummaryBean> contracts = storageQuery.getClientContracts(client.getClient().getOrganization().getId(), client
.getClient().getId(), client.getVersion());
+ // If empty, not ready
if (contracts.isEmpty()) {
- hasContracts = false;
+ return false;
}
-
- return isReady(client, hasContracts);
+ // None must be in unapproved state
+ return contracts.stream().allMatch(c -> c.getStatus() == ContractStatus.Created);
}
- /**
- * @see io.apiman.manager.api.core.IClientValidator#isReady(io.apiman.manager.api.beans.clients.ClientVersionBean, boolean)
- */
+ // /**
+ // * @see io.apiman.manager.api.core.IClientValidator#isReady(io.apiman.manager.api.beans.clients.ClientVersionBean, boolean)
+ // */
+ // @Override
+ // public boolean isReady(ClientVersionBean client, boolean hasContracts) throws Exception {
+ // return hasContracts;
+ // }
+
@Override
- public boolean isReady(ClientVersionBean client, boolean hasContracts) throws Exception {
- return hasContracts;
+ public ClientStatus determineStatus(ClientVersionBean cvb, List<ContractBean> contracts) {
+ if (contracts.isEmpty()) {
+ return ClientStatus.Created;
+ }
+ boolean anyAwaitingApproval = contracts.stream().anyMatch(c -> c.getStatus() == ContractStatus.AwaitingApproval);
+ if (anyAwaitingApproval) {
+ return ClientStatus.AwaitingApproval;
+ }
+ return ClientStatus.Ready;
}
/**

MODIFY
manager/ui/war/plugins/api-manager/html/client/client_entity.include
manager/ui/war/plugins/api-manager/html/client/client_entity.include
</div>
<div class="col-md-5" apiman-permission="clientAdmin">
<div>
- <div apiman-status="Created,Ready,Registered"><a apiman-i18n-key="ttdo-consume-apis" data-field="ttd_toConsumeApis" href="{{ pluginName }}/browse/apis">Search for APIs to consume</a></div>
- <div apiman-status="Created,Ready,Registered"><a apiman-i18n-key="ttdo-new-contract" data-field="ttd_toNewContract" href="{{ pluginName }}/new-contract">Create a new API contract for this Client App</a></div>
+ <div apiman-status="Created,Ready,Registered,AwaitingApproval"><a apiman-i18n-key="ttdo-consume-apis" data-field="ttd_toConsumeApis" href="{{ pluginName }}/browse/apis">Search for APIs to consume</a></div>
+ <div apiman-status="Created,Ready,Registered,AwaitingApproval"><a apiman-i18n-key="ttdo-new-contract" data-field="ttd_toNewContract" href="{{ pluginName }}/new-contract">Create a new API contract for this Client App</a></div>
<div><a apiman-i18n-key="ttdo-new-version" data-field="ttd_toNewVersion" href="{{ pluginName }}/orgs/{{ org.id }}/clients/{{ client.id}}/{{ version.version }}/new-version">Create a new version of this Client App (New Version)</a></div>
</div>
<!-- The Register Action -->

MODIFY
manager/ui/war/plugins/api-manager/html/dash.html
manager/ui/war/plugins/api-manager/html/dash.html
</ul>
</div>
</div>
+
+ <div class="dash-group">
+ <div class="title">
+ <i class="fa fa-code-fork"></i>
+ <span apiman-i18n-key="notifications">Developer Portal</span>
+ </div>
+ <div class="description" apiman-i18n-key="dash.devportal-description">
+ Manage your developer portal, including approving requests by developers to sign up to APIs
+ </div>
+ <div class="actions">
+ <ul>
+<!-- <li>Developer account approvals</li>-->
+ <li>API Signup Approval Requests</li>
+<!-- <li>Manage notifications -- probably should be in notifications area</li>-->
+ </ul>
+ </div>
+ </div>
+
</div>
</div>
</div> <!-- /container -->

MODIFY
manager/ui/war/plugins/api-manager/ts/api/api-contracts.ts
manager/ui/war/plugins/api-manager/ts/api/api-contracts.ts
/// <reference path="../rpc.ts"/>
module Apiman {
+ import ContractSummaryBean = ApimanRPC.ContractSummaryBean;
export var ApiContractsController = _module.controller("Apiman.ApiContractsController",
- ['$q', '$scope', '$location', 'PageLifecycle', 'ApiEntityLoader', 'OrgSvcs', 'Logger', '$routeParams', 'Configuration',
- ($q, $scope, $location, PageLifecycle, ApiEntityLoader, OrgSvcs, Logger, $routeParams, Configuration) => {
+ ['$q', '$scope', '$location', 'PageLifecycle', 'ApiEntityLoader', 'OrgSvcs', 'Logger', '$routeParams', 'Configuration', 'ContractService',
+ ($q, $scope, $location, PageLifecycle, ApiEntityLoader, OrgSvcs, Logger, $routeParams, Configuration, ContractService) => {
var params = $routeParams;
$scope.organizationId = params.org;
$scope.tab = 'contracts';
@@ -34,6 +35,18 @@ module Apiman {
})
});
+ $scope.approveContract = (contract: ContractSummaryBean) => {
+ Logger.debug("Attempting to approve contract: {0}", contract);
+ ContractService.approveContract(contract.contractId).then(
+ (_) => {
+ contract.status = 'Created';
+ },
+ (failure) => {
+
+ }
+ )
+ };
+
$scope.getNextPage = getNextPage;
PageLifecycle.loadPage('ApiContracts', 'apiView', pageData, $scope, function() {

MODIFY
manager/ui/war/plugins/api-manager/ts/client/client-contracts.ts
manager/ui/war/plugins/api-manager/ts/client/client-contracts.ts
/// <reference path="../apimanPlugin.ts"/>
/// <reference path="../rpc.ts"/>
-/// <reference path="../model/contract.d.ts"/>
-
-import {ContractSummaryBean} from "../model/contract";
+/// <reference path="../model/contract.model.ts"/>
module Apiman {
+ import ContractSummaryBean = ApimanRPC.ContractSummaryBean;
export var ClientContractsController = _module.controller('Apiman.ClientContractsController',
[
'$q',
@@ -17,7 +16,8 @@ module Apiman {
'Logger',
'$routeParams',
'Configuration',
- ($q, $scope, $location, $uibModal, PageLifecycle, ClientEntityLoader, OrgSvcs, Logger, $routeParams, Configuration) => {
+ 'ContractService',
+ ($q, $scope, $location, $uibModal, PageLifecycle, ClientEntityLoader, OrgSvcs, Logger, $routeParams, Configuration, ContractService) => {
var params = $routeParams;
$scope.organizationId = params.org;
@@ -137,6 +137,11 @@ module Apiman {
});
};
+ $scope.approveContract = (id: number) => {
+ Logger.debug("Approving {0}", id);
+ ContractService.approveContract(id);
+ };
+
PageLifecycle.loadPage('ClientContracts', 'clientView', pageData, $scope, function() {
PageLifecycle.setPageTitle('client-contracts', [ $scope.client.name ]);
});

MODIFY
manager/ui/war/plugins/api-manager/ts/forms/new-contract.ts
manager/ui/war/plugins/api-manager/ts/forms/new-contract.ts
@@ -23,7 +23,7 @@ module Apiman {
var plainVersions = [];
angular.forEach(versions, function (version) {
- if (version.status == 'Created' || version.status == 'Ready' || version.status == 'Registered') {
+ if (version.status === 'Created' || version.status === 'Ready' || version.status === 'Registered' || version.status === 'AwaitingApproval') {
plainVersions.push(version.version);
}
});

DELETE
manager/ui/war/plugins/api-manager/ts/model/contract.d.ts
- export interface ContractSummaryBean {
- contractId: number;
- clientOrganizationId: string;
- clientOrganizationName: string;
- clientId: string;
- clientName: string;
- clientVersion: string;
- apiOrganizationId: string;
- apiOrganizationName: string;
- apiId: string;
- apiName: string;
- apiVersion: string;
- apiDescription: string;
- planName: string;
- planId: string;
- planVersion: string;
- createdOn: Date;
- status: string; // ContractStatus enum
- }

ADD
manager/ui/war/plugins/api-manager/ts/model/contract.model.ts
manager/ui/war/plugins/api-manager/ts/model/contract.model.ts
MODIFY
manager/ui/war/plugins/api-manager/ts/rpc.ts
manager/ui/war/plugins/api-manager/ts/rpc.ts
@@ -49,66 +49,24 @@ module ApimanRPC {
});
}]);
- export interface ApiPlanSummaryBean {
- planId: string;
- planName: string;
- planDescription: string;
- version: string;
- exposeInPortal: boolean;
- requiresApproval: boolean;
- }
-
- export interface ApiGatewayBean {
- gatewayId: string
- }
-
- export interface ApiPlanBean {
- planId: string,
- version: string,
- exposeInPortal: boolean,
- requiresApproval: boolean
- }
-
- export interface UpdateApiVersionBean {
- endpoint: string;
- endpointType: string; // enum EndpointType
- endpointContentType: string // enum EndpointContentType;
- endpointProperties: Map<string, string>,
- gateways: ApiGatewayBean[], // Set<ApiGatewayBean>
- parsePayload: boolean,
- publicAPI: boolean,
- disableKeysStrip: boolean,
- plans: ApiPlanBean[],
- extendedDescription: string,
- exposeInPortal: boolean
+ export var ContractService = _module.factory('ContractService', ['$http', 'Configuration', '$q',
+ ($http, Configuration, $q) => {
+ return {
+ approveContract: (contractId: number): Promise<any> => {
+ return $http({
+ method: 'POST',
+ url: `${Configuration.api.endpoint}/actions/contracts`,
+ data: {
+ contractId: contractId,
+ status: 'AwaitingApproval'
+ } as ContractAction
+ }).then(
+ (success) => $q.resolve(success.data),
+ (failure) => $q.reject(failure)
+ );
}
-
- export interface ApiVersionBean {
- id: number,
- //api: ApiBean,
- status: string, // Enum ApiStatus
- endpoint: string,
- endpointType: string, //enum EndpointType
- endpointContentType: string, // enum EndpointContentType
- endpointProperties: Map<String, String>,
- gateways: ApiGatewayBean[],
- publicAPI: boolean,
- //apiDefinition: ApiDefinitionBean
- plans: ApiPlanBean[],
- version: string,
- createdBy: string,
- createdOn: string,
- modifiedBy: string,
- modifiedOn: string,
- publishedOn: string,
- retiredOn: string,
- definitionType: string // ApiDefinitionType
- parsePayload: boolean,
- disableKeysStrip: boolean,
- definitionUrl: string,
- exposeInPortal: boolean,
- extendedDescription: string
}
+ }]);
export var DevPortalService = _module.factory('DevPortalService', ['$http', 'Configuration', '$q',
($http, Configuration, $q) => {
@@ -146,13 +104,6 @@ module ApimanRPC {
}
}]);
- export interface BlobRef {
- id: string;
- name: string;
- mimeType: string;
- hash: number;
- }
-
export var BlobService = _module.factory('BlobService', ['$http', 'Configuration', '$q',
($http, Configuration, $q) => {
return {
@@ -432,7 +383,6 @@ module ApimanRPC {
}
}]);
-
export var ApiCatalogSvcs = _module.factory('ApiCatalogSvcs', ['$resource', 'Configuration',
function($resource, Configuration) {
return {
@@ -449,4 +399,97 @@ module ApimanRPC {
}
}]);
+ export interface ApiPlanSummaryBean {
+ planId: string;
+ planName: string;
+ planDescription: string;
+ version: string;
+ exposeInPortal: boolean;
+ requiresApproval: boolean;
+ }
+
+ export interface ApiGatewayBean {
+ gatewayId: string
+ }
+
+ export interface ApiPlanBean {
+ planId: string;
+ version: string;
+ exposeInPortal: boolean;
+ requiresApproval: boolean
+ }
+
+ export interface UpdateApiVersionBean {
+ endpoint: string;
+ endpointType: string; // enum EndpointType
+ endpointContentType: string // enum EndpointContentType;
+ endpointProperties: Map<string, string>;
+ gateways: ApiGatewayBean[]; // Set<ApiGatewayBean>
+ parsePayload: boolean;
+ publicAPI: boolean;
+ disableKeysStrip: boolean;
+ plans: ApiPlanBean[];
+ extendedDescription: string;
+ exposeInPortal: boolean
+ }
+
+ export interface ApiVersionBean {
+ id: number;
+ //api: ApiBean;
+ status: string; // Enum ApiStatus
+ endpoint: string;
+ endpointType: string; //enum EndpointType
+ endpointContentType: string; // enum EndpointContentType
+ endpointProperties: Map<string, string>;
+ gateways: ApiGatewayBean[];
+ publicAPI: boolean;
+ //apiDefinition: ApiDefinitionBean
+ plans: ApiPlanBean[];
+ version: string;
+ createdBy: string;
+ createdOn: string;
+ modifiedBy: string;
+ modifiedOn: string;
+ publishedOn: string;
+ retiredOn: string;
+ definitionType: string // ApiDefinitionType
+ parsePayload: boolean;
+ disableKeysStrip: boolean;
+ definitionUrl: string;
+ exposeInPortal: boolean;
+ extendedDescription: string
+ }
+
+ export interface ContractSummaryBean {
+ contractId: number;
+ clientOrganizationId: string;
+ clientOrganizationName: string;
+ clientId: string;
+ clientName: string;
+ clientVersion: string;
+ apiOrganizationId: string;
+ apiOrganizationName: string;
+ apiId: string;
+ apiName: string;
+ apiVersion: string;
+ apiDescription: string;
+ planName: string;
+ planId: string;
+ planVersion: string;
+ createdOn: Date;
+ status: string; // ContractStatus enum
+ }
+
+ export interface BlobRef {
+ id: string;
+ name: string;
+ mimeType: string;
+ hash: number;
+ }
+
+ export interface ContractAction {
+ contractId: number,
+ status: string
+ }
+
}

feat(ui): contract approvals flow