adf-cli command init-aae use the user based on the role (#5364)

* Reuse the common deployment and modeling api

* Add files to testing

* Pass parameters

* Travis initaae with correct parameters

* Travis initaae with correct parameters

* Add cli to the smart build
This commit is contained in:
Maurizio Vitale 2020-01-10 17:02:16 +00:00 committed by GitHub
parent 94e02c0552
commit 26677c6789
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 698 additions and 187 deletions

View File

@ -838,6 +838,20 @@
} }
} }
} }
},
"cli": {
"root": "lib/cli",
"sourceRoot": "lib/cli",
"projectType": "library",
"prefix": "adf",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "lib/cli/tsconfig.json"
}
}
}
} }
}, },
"defaultProject": "dist" "defaultProject": "dist"

View File

@ -141,7 +141,7 @@ adf-cli update-commit-sha --pathProject "$(pwd)" --skipGnu
The following command is in charge of Initializing the activiti cloud env with the default apps: The following command is in charge of Initializing the activiti cloud env with the default apps:
```bash ```bash
adf-cli init-aae-env --host "gateway_env" --oauth "identity_env" --identityHost "identity_env" --username "username" --password "password" adf-cli init-aae-env --host "gateway_env" --oauth "identity_env" --identityHost "identity_env" --modelerUsername "modelerusername" --modelerPassword "modelerpassword" --devopsUsername "devopsusername" --devopsPassword "devopspassword"
``` ```
If you want to add a new app the schema needs to be: If you want to add a new app the schema needs to be:

View File

@ -17,7 +17,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { ACTIVITI_CLOUD_APPS } from '@alfresco/adf-testing'; import { ACTIVITI_CLOUD_APPS, DeploymentAPI, ModelingAPI } from '@alfresco/adf-testing';
import * as program from 'commander'; import * as program from 'commander';
/* tslint:disable */ /* tslint:disable */
@ -28,39 +28,26 @@ import * as fs from 'fs';
import { logger } from './logger'; import { logger } from './logger';
export interface ConfigArgs { export interface ConfigArgs {
username: string; modelerUsername: string;
password: string; modelerPassword: string;
devopsUsername: string;
devopsPassword: string;
clientId: string; clientId: string;
host: string; host: string;
oauth: string; oauth: string;
identityHost: boolean; identityHost: boolean;
} }
let browser: any;
let deploymentAPI: DeploymentAPI;
let modelingAPI: ModelingAPI;
export const AAE_MICROSERVICES = [ export const AAE_MICROSERVICES = [
'deployment-service', 'deployment-service',
'modeling-service', 'modeling-service',
'dmn-service' 'dmn-service'
]; ];
async function getDeployedApplicationsByStatus(args: ConfigArgs, apiService: any, status: string) {
const url = `${args.host}/deployment-service/v1/applications`;
const pathParams = {}, queryParams = {status: status},
headerParams = {}, formParams = {}, bodyParam = {},
contentTypes = ['application/json'], accepts = ['application/json'];
let data;
try {
data = await apiService.oauth2Auth.callCustomApi(url, 'GET', pathParams, queryParams, headerParams, formParams, bodyParam,
contentTypes, accepts);
return data.list.entries;
} catch (error) {
logger.error(`Not possible get the applications from deployment-service ${JSON.stringify(error)} `);
process.exit(1);
}
}
async function healthCheck(args: ConfigArgs, apiService: any, nameService: string, result: any) { async function healthCheck(args: ConfigArgs, apiService: any, nameService: string, result: any) {
const url = `${args.host}/${nameService}/actuator/health`; const url = `${args.host}/${nameService}/actuator/health`;
@ -103,72 +90,50 @@ function getAlfrescoJsApiInstance(args: ConfigArgs) {
async function login(args: ConfigArgs, alfrescoJsApi: any) { async function login(args: ConfigArgs, alfrescoJsApi: any) {
logger.info(`Perform login...`); logger.info(`Perform login...`);
try { try {
await alfrescoJsApi.login(args.username, args.password); await alfrescoJsApi.login(args.modelerUsername, args.modelerPassword);
} catch (error) { } catch (error) {
logger.error(`Not able to login. Credentials ${args.username}:${args.password} are not valid`); logger.error(`Not able to login. Credentials ${args.modelerUsername}:${args.modelerPassword} are not valid`);
process.exit(1); process.exit(1);
} }
return alfrescoJsApi; return alfrescoJsApi;
} }
async function deployMissingApps(args: ConfigArgs, alfrescoJsApi: any) { async function deployMissingApps() {
const deployedApps = await getDeployedApplicationsByStatus(args, alfrescoJsApi, ''); const deployedApps = await deploymentAPI.getApplicationByStatus('');
const absentApps = findMissingApps(deployedApps); const absentApps = findMissingApps(deployedApps.list.entries);
if (absentApps.length > 0) { if (absentApps.length > 0) {
logger.warn(`Missing apps: ${JSON.stringify(absentApps)}`); logger.warn(`Missing apps: ${JSON.stringify(absentApps)}`);
await checkIfAppIsReleased(args, alfrescoJsApi, absentApps); await checkIfAppIsReleased(absentApps);
} else { } else {
logger.warn(`All the apps are correctly deployed`); logger.warn(`All the apps are correctly deployed`);
} }
} }
async function getAppProjects(args: ConfigArgs, apiService: any) { async function checkIfAppIsReleased(absentApps: any []) {
const url = `${args.host}/modeling-service/v1/projects?maxItems=200&skipCount=0`; const projectList = await modelingAPI.getProjects();
const pathParams = {}, queryParams = {},
headerParams = {}, formParams = {}, bodyParam = {},
contentTypes = ['application/json'], accepts = ['application/json'];
let data;
try {
data = await apiService.oauth2Auth.callCustomApi(url, 'GET', pathParams, queryParams, headerParams, formParams, bodyParam,
contentTypes, accepts);
return data.list.entries;
} catch (error) {
logger.error(`Not possible get the application from modeling-service ` + error);
process.exit(1);
}
}
async function checkIfAppIsReleased(args: ConfigArgs, apiService: any, absentApps: any []) {
const projectList = await getAppProjects(args, apiService);
let TIME = 5000; let TIME = 5000;
let noError = true; let noError = true;
for (let i = 0; i < absentApps.length; i++) { for (let i = 0; i < absentApps.length; i++) {
noError = true; noError = true;
const currentAbsentApp = absentApps[i]; const currentAbsentApp = absentApps[i];
const app = projectList.find((currentApp: any) => { const project = projectList.list.entries.find((currentApp: any) => {
return currentAbsentApp.name === currentApp.entry.name; return currentAbsentApp.name === currentApp.entry.name;
}); });
let projectRelease: any; let projectRelease: any;
if (app === undefined) { if (project === undefined) {
logger.warn('Missing project: Create the project for ' + currentAbsentApp.name); logger.warn('Missing project: Create the project for ' + currentAbsentApp.name);
try { try {
const uploadedApp = await importProjectApp(args, apiService, currentAbsentApp); projectRelease = await importProjectAndRelease(currentAbsentApp);
logger.warn('Project imported ' + currentAbsentApp.name);
if (uploadedApp) {
projectRelease = await releaseProject(args, apiService, uploadedApp);
}
} catch (error) { } catch (error) {
logger.info(`error status ${error.status}`); logger.info(`error status ${error.status}`);
if (error.status !== 409) { if (error.status !== 409) {
logger.info(`Not possible to upload the project ${app.name} status : ${JSON.stringify(error.status)} ${JSON.stringify(error.response.text)}`); logger.info(`Not possible to upload the project ${project.entry.name} status : ${JSON.stringify(error.status)} ${JSON.stringify(error.response.text)}`);
process.exit(1); process.exit(1);
} else { } else {
logger.error(`Not possible to upload the project because inconsistency CS - Modelling try to delete manually the node`); logger.error(`Not possible to upload the project because inconsistency CS - Modelling try to delete manually the node`);
@ -180,13 +145,13 @@ async function checkIfAppIsReleased(args: ConfigArgs, apiService: any, absentApp
TIME += 5000; TIME += 5000;
logger.info('Project ' + app.entry.name + ' found'); logger.info('Project ' + project.entry.name + ' found');
const projectReleaseList = await getReleaseAppProjectId(args, apiService, app.entry.id); const projectReleaseList = await modelingAPI.getProjectRelease(project.entry.id);
if (projectReleaseList.list.entries.length === 0) { if (projectReleaseList.list.entries.length === 0) {
logger.warn('Project needs release'); logger.warn('Project needs release');
projectRelease = await releaseProject(args, apiService, app); projectRelease = await modelingAPI.releaseProject(project.entry);
logger.warn(`Project released: ${projectRelease.id}`); logger.warn(`Project released: ${projectRelease.id}`);
} else { } else {
logger.info('Project already has release'); logger.info('Project already has release');
@ -203,139 +168,37 @@ async function checkIfAppIsReleased(args: ConfigArgs, apiService: any, absentApp
} }
if (noError) { if (noError) {
await checkDescriptorExist(args, apiService, currentAbsentApp.name); await checkDescriptorExist(currentAbsentApp.name);
await sleep(TIME); await sleep(TIME);
await deployApp(args, apiService, currentAbsentApp, projectRelease.entry.id); const deployPayload = {
'name': currentAbsentApp.name,
'releaseId': projectRelease.entry.id,
'security': currentAbsentApp.security,
'infrastructure': currentAbsentApp.infrastructure,
'variables': currentAbsentApp.variables
};
await deploymentAPI.deploy(deployPayload);
} }
} }
} }
async function deployApp(args: ConfigArgs, apiService: any, appInfo: any, projectReleaseId: string) { async function checkDescriptorExist(name: string) {
logger.warn(`Deploy app ${appInfo.name} with projectReleaseId ${projectReleaseId}`);
const url = `${args.host}/deployment-service/v1/applications`;
const pathParams = {};
const bodyParam = {
'name': appInfo.name,
'releaseId': projectReleaseId,
'security': appInfo.security,
'infrastructure': appInfo.infrastructure,
'variables': appInfo.variables
};
logger.debug(`Deploy with body: ${JSON.stringify(bodyParam)}`);
const headerParams = {}, formParams = {}, queryParams = {},
contentTypes = ['application/json'], accepts = ['application/json'];
try {
return await apiService.oauth2Auth.callCustomApi(url, 'POST', pathParams, queryParams, headerParams, formParams, bodyParam,
contentTypes, accepts);
} catch (error) {
logger.error(`Not possible to deploy the project ${appInfo.name} status : ${JSON.stringify(error.status)} ${JSON.stringify(error.response.text)}`);
// await deleteSiteByName(name);
process.exit(1);
}
}
async function checkDescriptorExist(args: ConfigArgs, apiService: any, name: string) {
logger.info(`Check descriptor ${name} exist in the list `); logger.info(`Check descriptor ${name} exist in the list `);
const descriptorList: [] = await getDescriptorList(args, apiService); const descriptorList = await deploymentAPI.getDescriptors();
descriptorList.forEach( async(descriptor: any) => { descriptorList.list.entries.forEach( async(descriptor: any) => {
if (descriptor.entry.name === name) { if (descriptor.entry.name === name) {
if (descriptor.entry.deployed === false) { if (descriptor.entry.deployed === false) {
await deleteDescriptor(args, apiService, descriptor.entry.name); await deploymentAPI.deleteDescriptor(descriptor.entry.name);
} }
} }
}); });
return false; return false;
} }
async function getDescriptorList(args: ConfigArgs, apiService: any) { async function importProjectAndRelease(app: any) {
const url = `${args.host}/deployment-service/v1/descriptors?page=0&size=50&sort=lastModifiedAt,desc`;
const pathParams = {}, queryParams = {},
headerParams = {}, formParams = {}, bodyParam = {},
contentTypes = ['application/json'], accepts = ['application/json'];
let data;
try {
data = await apiService.oauth2Auth.callCustomApi(url, 'GET', pathParams, queryParams, headerParams, formParams, bodyParam,
contentTypes, accepts);
return data.list.entries;
} catch (error) {
logger.error(`Not possible get the descriptors from deployment-service ${JSON.stringify(error)} `);
}
}
async function deleteDescriptor(args: ConfigArgs, apiService: any, name: string) {
logger.warn(`Delete the descriptor ${name}`);
const url = `${args.host}/deployment-service/v1/descriptors/${name}`;
const pathParams = {};
const bodyParam = {};
const headerParams = {}, formParams = {}, queryParams = {},
contentTypes = ['application/json'], accepts = ['application/json'];
try {
return await apiService.oauth2Auth.callCustomApi(url, 'DELETE', pathParams, queryParams, headerParams, formParams, bodyParam, contentTypes, accepts);
} catch (error) {
logger.error(`Not possible to delete the descriptor ${name} status : ${JSON.stringify(error.status)} ${JSON.stringify(error.response.text)}`);
}
}
async function releaseProject(args: ConfigArgs, apiService: any, app: any) {
const url = `${args.host}/modeling-service/v1/projects/${app.entry.id}/releases`;
logger.info(`Release ID ${app.entry.id}`);
const pathParams = {}, queryParams = {},
headerParams = {}, formParams = {}, bodyParam = {},
contentTypes = ['application/json'], accepts = ['application/json'];
try {
return await apiService.oauth2Auth.callCustomApi(url, 'POST', pathParams, queryParams, headerParams, formParams, bodyParam,
contentTypes, accepts);
} catch (error) {
logger.info(`Not possible to release the project ${app.entry.name} status : $ \n ${JSON.stringify(error.status)} \n ${JSON.stringify(error.response.text)}`);
process.exit(1);
}
}
async function getReleaseAppProjectId(args: ConfigArgs, apiService: any, projectId: string) {
const url = `${args.host}/modeling-service/v1/projects/${projectId}/releases`;
const pathParams = {}, queryParams = {},
headerParams = {}, formParams = {}, bodyParam = {},
contentTypes = ['application/json'], accepts = ['application/json'];
try {
return await apiService.oauth2Auth.callCustomApi(url, 'GET', pathParams, queryParams, headerParams, formParams, bodyParam,
contentTypes, accepts);
} catch (error) {
logger.error(`Not possible to get the release of the project ${projectId} ` + JSON.stringify(error));
process.exit(1);
}
}
async function importProjectApp(args: ConfigArgs, apiService: any, app: any) {
await getFileFromRemote(app.file_location, app.name); await getFileFromRemote(app.file_location, app.name);
logger.warn('Project imported ' + app.name);
const file = fs.createReadStream(`${app.name}.zip`).on('error', () => {logger.error(`${app.name}.zip does not exist`); }); const result = await modelingAPI.importAndReleaseProject(`${app.name}.zip`);
const url = `${args.host}/modeling-service/v1/projects/import`;
const pathParams = {}, queryParams = {},
headerParams = {}, formParams = {'file': file}, bodyParam = {},
contentTypes = ['multipart/form-data'], accepts = ['application/json'];
logger.warn(`import app ${app.file_location}`);
const result = await apiService.oauth2Auth.callCustomApi(url, 'POST', pathParams, queryParams, headerParams, formParams, bodyParam,
contentTypes, accepts);
deleteLocalFile(`${app.name}`); deleteLocalFile(`${app.name}`);
return result; return result;
} }
@ -380,27 +243,69 @@ async function sleep(time: number) {
return; return;
} }
async function initConfiguration(args: ConfigArgs) {
browser = {
params: {
config: {
log: true
},
adminapp: {
apiConfig: {
authType: 'OAUTH',
identityHost: args.identityHost,
oauth2: {
host: args.oauth,
authPath: '/protocol/openid-connect/token/',
clientId: args.clientId,
scope: 'openid',
implicitFlow: false,
redirectUri: ''
},
bpmHost: args.host,
providers: 'BPM'
},
modeler: args.modelerUsername,
modeler_password: args.modelerPassword,
devops: args.devopsUsername,
devops_password: args.devopsPassword
}
}
};
global['protractor'] = {browser: browser};
deploymentAPI = new DeploymentAPI();
modelingAPI = new ModelingAPI();
await deploymentAPI.setUp();
await modelingAPI.setUp();
}
export default async function (args: ConfigArgs) { export default async function (args: ConfigArgs) {
await main(args); await main(args);
} }
async function main(args) { async function main(args: ConfigArgs) {
program program
.version('0.1.0') .version('0.1.0')
.description('The following command is in charge of Initializing the activiti cloud env with the default apps' + .description('The following command is in charge of Initializing the activiti cloud env with the default apps' +
'adf-cli init-aae-env --host "gateway_env" --oauth "identity_env" --identityHost "identity_env" --username "username" --password "password"') 'adf-cli init-aae-env --host "gateway_env" --oauth "identity_env" --identityHost "identity_env" --modelerUsername "modelerusername" --modelerPassword "modelerpassword" --devopsUsername "devevopsusername" --devopsPassword "devopspassword"')
.option('-h, --host [type]', 'Host gateway') .option('-h, --host [type]', 'Host gateway')
.option('-o, --oauth [type]', 'Host sso server') .option('-o, --oauth [type]', 'Host sso server')
.option('--clientId[type]', 'sso client') .option('--clientId[type]', 'sso client')
.option('--username [type]', 'username') .option('--modelerUsername [type]', 'username of a user with role ACTIVIT_MODELER')
.option('--password [type]', 'password') .option('--modelerPassword [type]', 'modeler password')
.option('--devopsUsername [type]', 'username of a user with role ACTIVIT_DEVOPS')
.option('--devopsPassword [type]', 'devops password')
.parse(process.argv); .parse(process.argv);
if (process.argv.includes('-h') || process.argv.includes('--help')) { if (process.argv.includes('-h') || process.argv.includes('--help')) {
program.outputHelp(); program.outputHelp();
} }
await initConfiguration(args);
const alfrescoJsApi = getAlfrescoJsApiInstance(args); const alfrescoJsApi = getAlfrescoJsApiInstance(args);
await login(args, alfrescoJsApi); await login(args, alfrescoJsApi);
@ -412,7 +317,7 @@ async function main(args) {
if (result.isValid) { if (result.isValid) {
logger.error('The envirorment is up and running'); logger.error('The envirorment is up and running');
await deployMissingApps(args, alfrescoJsApi); await deployMissingApps();
} else { } else {
logger.error('The envirorment is not up'); logger.error('The envirorment is not up');
process.exit(1); process.exit(1);

View File

@ -181,7 +181,7 @@ function createTmpTable(podName: string, appName: string) {
function dropTmpTable(podName: string) { function dropTmpTable(podName: string) {
logger.info('Perform drop tmp table...'); logger.info('Perform drop tmp table...');
const query = `drop table query_table`; const query = `drop table IF EXISTS query_table`;
const response = exec('kubectl', [`exec`, `${podName}`, `--`, `psql`, `-U`, `alfresco`, `-d`, `postgres`, `-c`, `${query}` ], {}); const response = exec('kubectl', [`exec`, `${podName}`, `--`, `psql`, `-U`, `alfresco`, `-d`, `postgres`, `-c`, `${query}` ], {});
logger.info(response); logger.info(response);
} }
@ -200,10 +200,27 @@ function deleteRelease(podName: string) {
logger.info(response); logger.info(response);
} }
function deleteInvolvedGroupsRelease(podName: string) {
logger.info('Perform delete release involved groups...');
const query = `delete from release_involved_groups where release_entity_id in (select id from release where release.project_id in (select query_table.project_id from query_table))`;
const response = exec('kubectl', [`exec`, `${podName}`, `--`, `psql`, `-U`, `alfresco`, `-d`, `postgres`, `-c`, `${query}` ], {});
logger.info(response);
}
function deleteInvolvedUsersRelease(podName: string) {
logger.info('Perform delete release involved users...');
const query = `delete from release_involved_users where release_entity_id in (select id from release where release.project_id in (select query_table.project_id from query_table))`;
const response = exec('kubectl', [`exec`, `${podName}`, `--`, `psql`, `-U`, `alfresco`, `-d`, `postgres`, `-c`, `${query}` ], {});
logger.info(response);
}
function deleteAllReleases(podName: string, appName: string) { function deleteAllReleases(podName: string, appName: string) {
logger.info(podName, appName); logger.info(podName, appName);
dropTmpTable(podName);
createTmpTable(podName, appName); createTmpTable(podName, appName);
deleteLatestRelease(podName); deleteLatestRelease(podName);
deleteInvolvedGroupsRelease(podName);
deleteInvolvedUsersRelease(podName);
deleteRelease(podName); deleteRelease(podName);
dropTmpTable(podName); dropTmpTable(podName);
} }

View File

@ -20,3 +20,4 @@ export * from './pages/public-api';
export * from './models/public-api'; export * from './models/public-api';
export * from './dialog/public-api'; export * from './dialog/public-api';
export * from './utils/public-api'; export * from './utils/public-api';
export * from './structure/public-api';

View File

@ -0,0 +1,42 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AlfrescoApi } from '@alfresco/js-api';
import { browser } from 'protractor';
export abstract class Api {
public api: AlfrescoApi;
testConfig = browser.params;
constructor(root: string) {
this.api = this.configureApi(root);
}
private configureApi(root: string): AlfrescoApi {
const config = browser.params.adminapp.apiConfig;
return new AlfrescoApi({
provider: 'BPM',
authType: config.authType,
oauth2: config.oauth2,
hostBpm: config.bpmHost + '/' + root
});
}
abstract setUp(): Promise<Api>;
abstract tearDown();
}

View File

@ -0,0 +1,58 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { E2eRequestApiHelper } from './e2e-request-api.helper';
import { Api } from './api';
import { Logger } from '../utils/logger';
import { ResultSetPaging } from '@alfresco/js-api';
export class Application {
requestApiHelper: E2eRequestApiHelper;
endPoint = `/v1/applications/`;
constructor(api: Api) {
this.requestApiHelper = new E2eRequestApiHelper(api);
}
async deploy(model: any): Promise<any> {
await this.requestApiHelper.post(`${this.endPoint}`, { bodyParam: model});
Logger.info(`[Application] Application '${model.name}' was deployed successfully.`);
}
async delete(applicationId: string): Promise<void> {
await this.requestApiHelper.delete(`${this.endPoint}${applicationId}`);
Logger.info(`[Application] Application: '${applicationId}' was deleted successfully.`);
}
async deleteDescriptor(name: string): Promise<void> {
await this.requestApiHelper.delete(`v1/descriptors/${name}`);
Logger.info(`[Descriptor] Descriptor: '${name}' was deleted successfully.`);
}
async getDescriptors(): Promise<ResultSetPaging> {
Logger.info(`[Descriptor] Return descriptors`);
return this.requestApiHelper.get<ResultSetPaging>(`v1/descriptors`, {});
}
async getApplicationsByStatus(status: string): Promise<ResultSetPaging> {
Logger.info(`[Application] Return application by status: ${status}`);
return this.requestApiHelper.get<ResultSetPaging>(this.endPoint, {
queryParams: { status: status }
});
}
}

View File

@ -0,0 +1,69 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Api } from './api';
import { Application } from './application';
import { Logger } from '../utils/logger';
import { browser } from 'protractor';
import { ResultSetPaging } from '@alfresco/js-api';
export class DeploymentAPI extends Api {
public application: Application;
constructor(ROOT: string = 'deployment-service') {
super(ROOT);
}
async setUp(): Promise<DeploymentAPI> {
await this.login();
this.application = new Application(this);
return this;
}
async tearDown(): Promise<void> {
await this.api.logout();
}
private async login(): Promise<void> {
try {
await this.api.login(
browser.params.adminapp.devops,
browser.params.adminapp.devops_password
);
} catch (error) {
Logger.error(error);
}
}
async deploy(releasedProject: any): Promise<void> {
await this.application.deploy(releasedProject);
}
async deleteDescriptor(name: string): Promise<void> {
await this.application.deleteDescriptor(name);
}
async getDescriptors(): Promise<ResultSetPaging> {
const descriptors = await this.application.getDescriptors();
return descriptors;
}
async getApplicationByStatus(status: string): Promise<ResultSetPaging> {
const applications = this.application.getApplicationsByStatus(status);
return applications;
}
}

View File

@ -0,0 +1,112 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Oauth2Auth } from '@alfresco/js-api/src/authentication/oauth2Auth';
export interface E2eRequestApiHelperOptions {
pathParams?: { [key: string]: any };
queryParams?: { [key: string]: any };
headerParams?: { [key: string]: any };
formParams?: { [key: string]: any };
bodyParam?: { [key: string]: any };
contentTypes?: string[];
accepts?: string[];
returnType?: any;
contextRoot?: string;
responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text';
}
function getDefaultOptions(): E2eRequestApiHelperOptions {
return {
pathParams: {},
queryParams: {},
headerParams: {},
formParams: {},
bodyParam: {},
contentTypes: ['application/json'],
accepts: ['application/json'],
returnType: undefined
};
}
export class E2eRequestApiHelper {
api: Oauth2Auth;
constructor(private backend) {
this.api = backend.api.oauth2Auth;
}
private buildUrl(endPoint: string): string {
const trimSlash = (str: string) => str.replace(/^\/|\/$/g, '');
const host = this.backend.api.config.hostBpm;
const path = '/' + trimSlash(endPoint);
return `${host}${path}`;
}
public get<T>(endPoint: string, overriddenOptions?: E2eRequestApiHelperOptions): PromiseLike<T> {
return this.request<T>('GET', endPoint, overriddenOptions);
}
public post<T>(endPoint: string, overriddenOptions?: E2eRequestApiHelperOptions): PromiseLike<T> {
return this.request<T>('POST', endPoint, overriddenOptions);
}
public put<T>(endPoint: string, overriddenOptions?: E2eRequestApiHelperOptions): PromiseLike<T> {
return this.request<T>('PUT', endPoint, overriddenOptions);
}
public delete<T>(endPoint: string, overriddenOptions?: E2eRequestApiHelperOptions): PromiseLike<T> {
return this.request<T>('DELETE', endPoint, overriddenOptions);
}
private request<T>(httpMethod: string, endPoint: string, overriddenOptions?: E2eRequestApiHelperOptions): PromiseLike<T> {
const options = {
...getDefaultOptions(),
...overriddenOptions
};
const {
pathParams,
queryParams,
headerParams,
formParams,
bodyParam,
contentTypes,
accepts,
returnType,
contextRoot,
responseType
} = options;
return this.api.callCustomApi(
this.buildUrl(endPoint),
httpMethod,
pathParams,
queryParams,
headerParams,
formParams,
bodyParam,
contentTypes,
accepts,
returnType,
contextRoot,
responseType
);
}
}

View File

@ -0,0 +1,78 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Api } from './api';
import { Project } from './project';
import { Logger } from '../utils/logger';
import { browser } from 'protractor';
import { NodeEntry, ResultSetPaging } from '@alfresco/js-api';
export class ModelingAPI extends Api {
public project: Project;
constructor(ROOT: string = 'modeling-service') {
super(ROOT);
}
async setUp(): Promise<ModelingAPI> {
await this.login();
this.project = new Project(this);
return this;
}
async tearDown(): Promise<void> {
await this.api.logout();
}
private async login(): Promise<void> {
try {
await this.api.login(
browser.params.adminapp.modeler,
browser.params.adminapp.modeler_password
);
} catch (error) {
Logger.error(error);
}
}
async createProject(): Promise<NodeEntry> {
const project = await this.project.create();
return project;
}
async releaseProject(project: any): Promise<NodeEntry> {
const releasedProject = await this.project.release(project.entry.id);
return releasedProject;
}
async getProjectRelease(projectId: string): Promise<ResultSetPaging> {
const releasedProject = await this.project.getProjectRelease(projectId);
return releasedProject;
}
async importAndReleaseProject(absoluteFilePath: string): Promise<NodeEntry> {
const project = await this.project.import(absoluteFilePath);
const releasedProject = await this.project.release(project.entry.id);
return releasedProject;
}
async getProjects(): Promise<ResultSetPaging> {
const projects = await this.project.searchProjects();
return projects;
}
}

View File

@ -0,0 +1,132 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { browser } from 'protractor';
import { ModelingAPI } from './modeling-api';
import { NodeEntry, ResultSetPaging } from '@alfresco/js-api';
import { UtilApi } from './utilapi';
import { E2eRequestApiHelper, E2eRequestApiHelperOptions } from './e2e-request-api.helper';
import * as fs from 'fs';
import { StringUtil } from '../utils/string.util';
import { Logger } from '../utils/logger';
export class Project {
requestApiHelper: E2eRequestApiHelper;
endPoint = '/v1/projects/';
namePrefix: string = browser.params.namePrefix;
constructor(api: ModelingAPI) {
this.requestApiHelper = new E2eRequestApiHelper(api);
}
async create(modelName: string = this.getRandomName()): Promise<NodeEntry> {
const project = await this.requestApiHelper
.post<NodeEntry>(this.endPoint, {bodyParam: { name: modelName }});
Logger.info(
`[Project] Project created with name: ${project.entry.name} and id: ${
project.entry.id
}.`
);
return project;
}
async createAndWaitUntilAvailable(modelName: string = this.getRandomName()): Promise<NodeEntry> {
try {
const project = await this.create(modelName);
await this.retrySearchProject(project.entry.id);
return project;
} catch (error) {
Logger.error(`[Project] Create and wait for project to be available failed!`);
throw error;
}
}
async get(projectId: string): Promise<NodeEntry> {
return this.requestApiHelper.get<NodeEntry>(`/v1/projects/${projectId}`);
}
async delete(projectId: string): Promise<void> {
await this.requestApiHelper.delete(`/v1/projects/${projectId}`);
Logger.info(
`[Project] Project '${projectId}' was deleted successfully.`
);
}
async release(projectId: string): Promise<any> {
try {
const release = await this.requestApiHelper
.post(`/v1/projects/${projectId}/releases`);
Logger.info(`[Project] Project '${projectId}' was released.`);
return release;
} catch (error) {
Logger.error(`[Project] Release project failed!`);
throw error;
}
}
async getProjectRelease(projectId: string): Promise<any> {
try {
return await this.requestApiHelper
.get<ResultSetPaging>(`/v1/projects/${projectId}/releases`);
} catch (error) {
Logger.error(`[Project] Not able to fetch project release!`);
throw error;
}
}
async import(projectFilePath: string): Promise<NodeEntry> {
const fileContent = await fs.createReadStream(projectFilePath);
const requestOptions: E2eRequestApiHelperOptions = {
formParams: { file: fileContent },
contentTypes: ['multipart/form-data']
};
try {
const project = await this.requestApiHelper
.post<NodeEntry>(`/v1/projects/import`, requestOptions);
Logger.info(`[Project] Project imported with name '${project.entry.name}' and id '${project.entry.id}'.`);
return project;
} catch (error) {
Logger.error(`[Project] Import project failed!`);
throw error;
}
}
async searchProjects(): Promise<ResultSetPaging> {
Logger.info(`[Project] Waiting created project to be ready for listing.`);
return this.requestApiHelper.get<ResultSetPaging>(this.endPoint, {
queryParams: { maxItems: 1000 }
});
}
private async retrySearchProject(modelId: string): Promise<{}> {
const predicate = (result: ResultSetPaging) => {
const foundModel = result.list.entries.find(model => {
return model.entry.id === modelId;
});
return !!foundModel;
};
const apiCall = () => this.searchProjects();
return UtilApi.waitForApi(apiCall, predicate);
}
private getRandomName(): string {
return this.namePrefix + StringUtil.generateRandomString(5).toLowerCase();
}
}

View File

@ -0,0 +1,21 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './api';
export * from './utilapi';
export * from './deployment-api';
export * from './modeling-api';

View File

@ -0,0 +1,44 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type ApiResultPredicate<T> = (result: T) => boolean;
export type ApiCall<T> = () => Promise<T>;
export class UtilApi {
static async waitForApi<T>(apiCall: ApiCall<T>, predicate: ApiResultPredicate<T>) {
const apiCallWithPredicateChecking = async () => {
const apiCallResult = await apiCall();
if (predicate(apiCallResult)) {
return Promise.resolve(apiCallResult);
} else {
return Promise.reject(apiCallResult);
}
};
return UtilApi.retryCall(apiCallWithPredicateChecking);
}
static retryCall(fn: () => Promise<any>, retry: number = 30, delay: number = 1000): Promise<any> {
const pause = (duration: number) => new Promise((res) => setTimeout(res, duration));
const run = (retries: number) => {
return fn().catch((err) => (retries > 1 ? pause(delay).then(() => run(retries - 1)) : Promise.reject(err)));
};
return run(retry);
}
}

View File

@ -37,6 +37,9 @@
}, },
"testing": { "testing": {
"tags": [] "tags": []
},
"cli": {
"tags": []
} }
} }
} }

View File

@ -84,7 +84,7 @@ done
for i in "${libs[@]}" for i in "${libs[@]}"
do do
if [ "$i" == "core" ] ; then if [ "$i" == "core" ] ; then
AFFECTED_LIBS="core$ content-services$ process-services$ process-services-cloud$ insights$ extensions$ testing$" AFFECTED_LIBS="core$ content-services$ process-services$ process-services-cloud$ insights$ extensions$ testing$ cli$"
echo "${AFFECTED_LIBS}" echo "${AFFECTED_LIBS}"
exit 0 exit 0
fi fi
@ -122,7 +122,6 @@ do
fi fi
done done
#process-services-cloud #process-services-cloud
for i in "${libs[@]}" for i in "${libs[@]}"
do do
@ -131,4 +130,12 @@ do
fi fi
done done
#cli
for i in "${libs[@]}"
do
if [ "$i" == "cli" ] ; then
AFFECTED_LIBS=$AFFECTED_LIBS" cli$"
fi
done
echo "${AFFECTED_LIBS}" echo "${AFFECTED_LIBS}"

View File

@ -108,3 +108,11 @@ do
./scripts/build/build-testing.sh || exit 1; ./scripts/build/build-testing.sh || exit 1;
fi fi
done done
#cli
for i in "${libs[@]}"
do
if [ "$i" == "cli$" ] ; then
./scripts/build/build-cli.sh || exit 1;
fi
done

View File

@ -15,7 +15,7 @@ AFFECTED_E2E="$(./scripts/git-util/affected-folder.sh -b $TRAVIS_BRANCH -f "e2e/
RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST_BPM" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e "$E2E_EMAIL" -host_sso "$E2E_HOST_SSO" -host_bpm "$E2E_HOST_BPM" -host_identity "$E2E_HOST_IDENTITY" -identity_admin_email "$E2E_ADMIN_EMAIL_IDENTITY" -identity_admin_password "$E2E_ADMIN_PASSWORD_IDENTITY" -prefix $TRAVIS_BUILD_NUMBER -b --use-dist -m 3 ) RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST_BPM" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e "$E2E_EMAIL" -host_sso "$E2E_HOST_SSO" -host_bpm "$E2E_HOST_BPM" -host_identity "$E2E_HOST_IDENTITY" -identity_admin_email "$E2E_ADMIN_EMAIL_IDENTITY" -identity_admin_password "$E2E_ADMIN_PASSWORD_IDENTITY" -prefix $TRAVIS_BUILD_NUMBER -b --use-dist -m 3 )
./node_modules/@alfresco/adf-cli/bin/adf-cli init-aae-env --host "$E2E_HOST_BPM" --oauth "$E2E_HOST_SSO" --username "$E2E_USERNAME" --password "$E2E_PASSWORD" --clientId 'activiti' || exit 1 ./node_modules/@alfresco/adf-cli/bin/adf-cli init-aae-env --host "$E2E_HOST_BPM" --oauth "$E2E_HOST_SSO" --modelerUsername "$E2E_MODELER_USERNAME" --modelerPassword "$E2E_MODELER_PASSWORD" --devopsUsername "$E2E_DEVOPS_USERNAME" --devopsPassword "$E2E_DEVOPS_PASSWORD" --clientId 'activiti' || exit 1
node ./scripts/check-env/check-cs-env.js --host "$E2E_HOST_BPM" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1 node ./scripts/check-env/check-cs-env.js --host "$E2E_HOST_BPM" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1
if [[ $AFFECTED_LIBS =~ "testing" || $AFFECTED_LIBS =~ "$CONTEXT_ENV" || $TRAVIS_PULL_REQUEST == "false" ]]; if [[ $AFFECTED_LIBS =~ "testing" || $AFFECTED_LIBS =~ "$CONTEXT_ENV" || $TRAVIS_PULL_REQUEST == "false" ]];