diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 83bfd0d..114dce4 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -80,7 +80,26 @@ your profiles by running the ***content-cli profile list*** command. | Note: Please do not use blanks in profile names | |-------------------------------------------------| -#### API Token +#### Profile Types +You can create profiles of two types: using OAuth (Device Code +or Client Credentials) or using API Tokens (Application Key / API Key): + +##### OAuth + +OAuth supports with two grant types: Device Code & Client Credentials. + +With Device Code, creating the profile will trigger an authorization flow +(using the OAuth 2.0 Device code). You will be prompted to follow an authorization +link where you must authorize the **Content CLI** to be able to access the EMS environment +on your behalf. + +With Client Credentials, you need to provide the credentials (Client ID, Client Secret) configured for your OAuth client. +You can create and configure an OAuth clients in the `Admin & Settings` section of your EMS account, under `Applications`. +The OAuth client needs to have the following scopes configured: studio, integration.data-pools, action-engine.projects. +After creating an OAuth client, you should assign it the permissions necessary for the respective commands. More +information on registering OAuth clients can be found [here](https://docs.celonis.com/en/registering-oauth-client.html). + +##### API Token You can choose between two different options when asked for an API token. The first option is to use an API key, which identifies the user that created diff --git a/package.json b/package.json index bc1f199..607d138 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@celonis/content-cli", - "version": "0.8.5", + "version": "0.10.0", "description": "CLI Tool to help manage content in Celonis EMS", "main": "content-cli.js", "bin": { @@ -20,6 +20,7 @@ "axios": "1.6.2", "commander": "^6.0.0", "form-data": "4.0.0", + "openid-client": "^5.6.1", "hpagent": "^1.2.0", "semver": "^7.3.2", "valid-url": "^1.0.9", diff --git a/src/commands/objective.command.ts b/src/commands/objective.command.ts deleted file mode 100644 index d1ecce5..0000000 --- a/src/commands/objective.command.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ContentService } from "../services/content.service"; -import { ObjectiveManagerFactory } from "../content/factory/objective-manager.factory"; - -export class ObjectiveCommand { - private contentService = new ContentService(); - private objectiveManagerFactory = new ObjectiveManagerFactory(); - - public async pullObjective(profile: string, id: string): Promise { - await this.contentService.pull(profile, this.objectiveManagerFactory.createManager(id, null)); - } - - public async pushObjective(profile: string, filename: string): Promise { - await this.contentService.push(profile, this.objectiveManagerFactory.createManager(null, filename)); - } -} diff --git a/src/commands/profile.command.ts b/src/commands/profile.command.ts index 87856b6..5a51ff8 100644 --- a/src/commands/profile.command.ts +++ b/src/commands/profile.command.ts @@ -1,8 +1,8 @@ import { QuestionService } from "../services/question.service"; -import { Profile } from "../interfaces/profile.interface"; +import {Profile, ProfileType} from "../interfaces/profile.interface"; import { ProfileService } from "../services/profile.service"; import { ProfileValidator } from "../validators/profile.validator"; -import { logger } from "../util/logger"; +import { FatalError, logger } from "../util/logger"; export class ProfileCommand { private profileService = new ProfileService(); @@ -11,8 +11,27 @@ export class ProfileCommand { const profile: Profile = {} as Profile; profile.name = await QuestionService.ask("Name of the profile: "); profile.team = await QuestionService.ask("Your team (please provide the full url): "); - profile.apiToken = await QuestionService.ask("Your api token: "); + const type = await QuestionService.ask("Profile type: OAuth Device Code (1), OAuth Client Credentials (2) or Application Key / API Key (3): " ); + switch (type) { + case "1": + profile.type = ProfileType.DEVICE_CODE; + break; + case "2": + profile.type = ProfileType.CLIENT_CREDENTIALS; + profile.clientId = await QuestionService.ask("Your client id: "); + profile.clientSecret = await QuestionService.ask("Your client secret: "); + break; + case "3": + profile.type = ProfileType.KEY; + profile.apiToken = await QuestionService.ask("Your api token: "); + break; + default: + logger.error(new FatalError("Invalid type")); + break; + } profile.authenticationType = await ProfileValidator.validateProfile(profile); + await this.profileService.authorizeProfile(profile); + this.profileService.storeProfile(profile); if (setAsDefault) { await this.makeDefaultProfile(profile.name); diff --git a/src/commands/widget-sourcemaps.command.ts b/src/commands/widget-sourcemaps.command.ts deleted file mode 100644 index 893559e..0000000 --- a/src/commands/widget-sourcemaps.command.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { WidgetSourcemapsManagerFactory } from "../content/factory/widget-sourcemaps-manager.factory"; - -export class WidgetSourcemapsCommand { - private widgetSourcemapsFactory = new WidgetSourcemapsManagerFactory(); - - public async pushSourceMaps(): Promise { - await this.widgetSourcemapsFactory.createManager().push(); - } -} diff --git a/src/content-cli-pull.ts b/src/content-cli-pull.ts index 6c24289..41a081e 100644 --- a/src/content-cli-pull.ts +++ b/src/content-cli-pull.ts @@ -1,6 +1,5 @@ import { AnalysisCommand } from "./commands/analysis.command"; import { SkillCommand } from "./commands/skill.command"; -import { ObjectiveCommand } from "./commands/objective.command"; import { DataPoolCommand } from "./commands/data-pool.command"; import { AssetCommand } from "./commands/asset.command"; import { PackageCommand } from "./commands/package.command"; @@ -55,20 +54,6 @@ class Pull { return program; } - public static objective(program: CommanderStatic): CommanderStatic { - program - .command("objective") - .description("Command to pull an objective") - .option("-p, --profile ", "Profile which you want to use to pull the objective") - .requiredOption("--id ", "Id of the objective you want to pull") - .action(async cmd => { - await new ObjectiveCommand().pullObjective(cmd.profile, cmd.id); - process.exit(); - }); - - return program; - } - public static dataPool(program: CommanderStatic): CommanderStatic { program .command("data-pool") @@ -118,7 +103,6 @@ class Pull { Pull.analysis(commander); Pull.analysisBookmarks(commander); Pull.skill(commander); -Pull.objective(commander); Pull.dataPool(commander); Pull.asset(commander); Pull.package(commander); diff --git a/src/content-cli-push.ts b/src/content-cli-push.ts index 80a0b6c..0f2690b 100644 --- a/src/content-cli-push.ts +++ b/src/content-cli-push.ts @@ -9,7 +9,6 @@ import { DataPoolCommand } from "./commands/data-pool.command"; import { AssetCommand } from "./commands/asset.command"; import { PackageCommand } from "./commands/package.command"; import { CTPCommand } from "./commands/ctp.command"; -import { WidgetSourcemapsCommand } from "./commands/widget-sourcemaps.command"; import { AnalysisBookmarksCommand } from "./commands/analysis-bookmarks.command"; import { execSync } from "child_process"; import { GracefulError, logger } from "./util/logger"; @@ -134,18 +133,6 @@ class Push { return program; } - public static widgetSourcemaps(program: CommanderStatic): CommanderStatic { - program - .command("widget-sourcemaps") - .description("Command to upload sourcemaps to Datadog RUM") - .action(async () => { - await new WidgetSourcemapsCommand().pushSourceMaps(); - process.exit(); - }); - - return program; - } - public static dataPool(program: CommanderStatic): CommanderStatic { program .command("data-pool") @@ -244,7 +231,6 @@ Push.asset(commander); Push.assets(commander); Push.package(commander); Push.packages(commander); -Push.widgetSourcemaps(commander); commander.parse(process.argv); diff --git a/src/content/factory/objective-manager.factory.ts b/src/content/factory/objective-manager.factory.ts deleted file mode 100644 index 7dbb2c5..0000000 --- a/src/content/factory/objective-manager.factory.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import { FatalError, logger } from "../../util/logger"; -import { ObjectiveManager } from "../manager/objective.manager"; - -export class ObjectiveManagerFactory { - public createManager(id: string, filename: string): ObjectiveManager { - const objectiveManager = new ObjectiveManager(); - objectiveManager.id = id; - if (filename !== null) { - objectiveManager.content = this.readFile(filename); - } - return objectiveManager; - } - - private readFile(filename: string): string { - if (!fs.existsSync(path.resolve(process.cwd(), filename))) { - logger.error(new FatalError("The provided file does not exit")); - } - return fs.readFileSync(path.resolve(process.cwd(), filename), { encoding: "utf-8" }); - } -} diff --git a/src/content/factory/widget-sourcemaps-manager.factory.ts b/src/content/factory/widget-sourcemaps-manager.factory.ts deleted file mode 100644 index b09728c..0000000 --- a/src/content/factory/widget-sourcemaps-manager.factory.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as path from "path"; -import { FatalError, logger } from "../../util/logger"; -import { WidgetSourcemapsManager } from "../manager/widget-sourcemaps.manager"; -import { WidgetManagerFactory } from "./widget-manager.factory"; - -export class WidgetSourcemapsManagerFactory { - private widgetManagerFactory = new WidgetManagerFactory(); - - public createManager(): WidgetSourcemapsManager { - const widgetSourcemapsManager = new WidgetSourcemapsManager(); - const manifest = this.widgetManagerFactory.fetchManifest(); - - if (!manifest) { - logger.error(new FatalError("Missing manifest file.")); - } - - this.widgetManagerFactory.validateManifest(manifest); - - widgetSourcemapsManager.distPath = path.resolve(process.cwd()); - widgetSourcemapsManager.service = manifest.key; - widgetSourcemapsManager.releaseVersion = manifest.version; - return widgetSourcemapsManager; - } -} diff --git a/src/content/manager/analysis-bookmarks.manager.ts b/src/content/manager/analysis-bookmarks.manager.ts index 93cea29..81a820a 100644 --- a/src/content/manager/analysis-bookmarks.manager.ts +++ b/src/content/manager/analysis-bookmarks.manager.ts @@ -4,7 +4,7 @@ import * as fs from "fs"; import * as FormData from "form-data"; export class AnalysisBookmarksManager extends BaseManager { - private static BASE_URL = "/process-analytics/api/bookmarks/"; + private static BASE_URL = "/process-analytics/api/bookmarks"; private static ANALYSIS_BOOKMARKS_FILE_PREFIX = "studio_analysis_bookmarks_"; private _analysisId: string; diff --git a/src/content/manager/objective.manager.ts b/src/content/manager/objective.manager.ts deleted file mode 100644 index eb4324d..0000000 --- a/src/content/manager/objective.manager.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { BaseManager } from "./base.manager"; -import { ManagerConfig } from "../../interfaces/manager-config.interface"; - -export class ObjectiveManager extends BaseManager { - private static BASE_URL = "/transformation-center/api"; - private _id: string; - private _content: string; - - public get content(): string { - return this._content; - } - - public set content(value: string) { - this._content = value; - } - - public get id(): string { - return this._id; - } - - public set id(value: string) { - this._id = value; - } - - public getConfig(): ManagerConfig { - return { - pushUrl: this.profile.team.replace(/\/?$/, `${ObjectiveManager.BASE_URL}/objective-kpis/import`), - pullUrl: this.profile.team.replace(/\/?$/, `${ObjectiveManager.BASE_URL}/objectives/export?id=${this.id}`), - exportFileName: "objective_" + this.id + ".json", - onPushSuccessMessage: (data: any): string => { - return "Objective was pushed successfully. New ID: " + data.analysis.id; - }, - }; - } - - public getBody(): any { - return { - useDataModelId: "dummy", - serializedObjectiveExports: this.content, - }; - } - - protected getSerializedFileContent(data: any): string { - return JSON.stringify(data); - } -} diff --git a/src/content/manager/skill.manager.ts b/src/content/manager/skill.manager.ts index 555e341..fd6ae23 100644 --- a/src/content/manager/skill.manager.ts +++ b/src/content/manager/skill.manager.ts @@ -3,7 +3,7 @@ import { ManagerConfig } from "../../interfaces/manager-config.interface"; import * as FormData from "form-data"; export class SkillManager extends BaseManager { - private static BASE_URL = "/action-engine/api/projects/"; + private static BASE_URL = "/action-engine/api/projects"; private _skillId: string; private _projectId: string; private _content: any; diff --git a/src/content/manager/widget-sourcemaps.manager.ts b/src/content/manager/widget-sourcemaps.manager.ts deleted file mode 100644 index 22173e9..0000000 --- a/src/content/manager/widget-sourcemaps.manager.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { exec, execSync } from "child_process"; -import { GracefulError, logger } from "../../util/logger"; -import { ManagerConfig } from "../../interfaces/manager-config.interface"; -import { BaseManager } from "./base.manager"; - -export class WidgetSourcemapsManager extends BaseManager { - public service: string; - public releaseVersion: string; - public distPath: string; - - public async push(): Promise { - return this.pushWidgetSourcemaps(`assets/widgets/${this.service}`, `assets/widgets/${this.service}`); - } - - private async pushWidgetSourcemaps(sourcemapsPath: string, distPathPostfix: string): Promise { - return new Promise((resolve, reject) => { - if (!process.env.DATADOG_SITE) { - logger.error(new GracefulError("Missing DATADOG_SITE")); - } else if (!process.env.DATADOG_API_KEY) { - logger.error(new GracefulError("Missing DATADOG_API_KEY")); - } - - const datadogCiPath = require.resolve("@datadog/datadog-ci/dist/cli.js"); - const commandLines = [ - `node ${datadogCiPath} sourcemaps upload .`, - `--service=package-manager`, - `--release-version=1.0.0`, - "--disable-git", - ]; - - const datadogCommandLine = [...commandLines, "--minified-path-prefix=/package-manager/"].join(" "); - exec(datadogCommandLine, (error, stdout) => { - if (error) { - logger.error(new GracefulError(error.message)); - } else { - logger.info(new GracefulError(stdout)); - } - - if (process.env.AWS_ACCESS_KEY_ID_CDN && process.env.AWS_SECRET_ACCESS_KEY_CDN) { - try { - const uploadSourcemapsStdout = execSync( - [ - ...commandLines, - `--minified-path-prefix=https://static.celonis.cloud/static/package-manager/`, - ].join(" ") - ); - logger.info(uploadSourcemapsStdout); - } catch (error) { - logger.error(new GracefulError(error.stderr?.toString() || error.message)); - } - } - - return resolve(error || stdout); - }); - }); - } - - protected getConfig(): ManagerConfig { - return {}; - } - - protected getBody(): object { - return {}; - } - - protected getSerializedFileContent(data: any): string { - return ""; - } -} diff --git a/src/interfaces/batch-export-import-constants.ts b/src/interfaces/batch-export-import-constants.ts index 1407566..a0c815e 100644 --- a/src/interfaces/batch-export-import-constants.ts +++ b/src/interfaces/batch-export-import-constants.ts @@ -1,11 +1,11 @@ export enum BatchExportImportConstants { - STUDIO_FILE_NAME = "studio.yml", - VARIABLES_FILE_NAME = "variables.yml", - MANIFEST_FILE_NAME = "manifest.yml", + STUDIO_FILE_NAME = "studio.json", + VARIABLES_FILE_NAME = "variables.json", + MANIFEST_FILE_NAME = "manifest.json", STUDIO = "STUDIO", APP_MODE_VIEWER = "VIEWER", ZIP_EXTENSION = ".zip", - YAML_EXTENSION = ".yml", + JSON_EXTENSION = ".json", NODES_FOLDER_NAME = "nodes/", SCENARIO_NODE = "SCENARIO" } \ No newline at end of file diff --git a/src/interfaces/package-export-transport.ts b/src/interfaces/package-export-transport.ts index fb1ec7a..9f20c51 100644 --- a/src/interfaces/package-export-transport.ts +++ b/src/interfaces/package-export-transport.ts @@ -51,7 +51,7 @@ export interface NodeExportTransport { name: string; type: string; exportSerializationType: string; - configuration: string; + configuration: NodeConfiguration; schemaVersion: number; spaceId: string; @@ -60,8 +60,9 @@ export interface NodeExportTransport { serializedDocument?: Buffer; } -export interface NodeSerializedContent { - variables: VariableDefinition[] +export interface NodeConfiguration { + variables?: VariableDefinition[]; + [key: string]: any; } export interface StudioPackageManifest { diff --git a/src/interfaces/profile.interface.ts b/src/interfaces/profile.interface.ts index 59f0392..ae21f54 100644 --- a/src/interfaces/profile.interface.ts +++ b/src/interfaces/profile.interface.ts @@ -1,13 +1,34 @@ export interface Profile { name: string; team: string; + type: ProfileType; apiToken: string; authenticationType: AuthenticationType; + clientId?: string; + clientSecret?: string; + scopes?: string[]; + clientAuthenticationMethod?: ClientAuthenticationMethod; + refreshToken?: string; + expiresAt?: number; } export type AuthenticationType = "Bearer" | "AppKey"; +export type ProfileType = "Device Code" | "Client Credentials" | "Key"; + +export type ClientAuthenticationMethod = "client_secret_basic" | "client_secret_post"; // tslint:disable-next-line:variable-name export const AuthenticationType: { [key: string]: AuthenticationType } = { BEARER: "Bearer", APPKEY: "AppKey", }; +// tslint:disable-next-line:variable-name +export const ProfileType: { [key: string]: ProfileType } = { + DEVICE_CODE: "Device Code", + CLIENT_CREDENTIALS: "Client Credentials", + KEY: "Key" +}; +// tslint:disable-next-line:variable-name +export const ClientAuthenticationMethod: { [key: string]: ClientAuthenticationMethod } = { + CLIENT_SECRET_BASIC: "client_secret_basic", + CLIENT_SECRET_POST: "client_secret_post", +}; diff --git a/src/services/package-manager/batch-import-export-service.ts b/src/services/package-manager/batch-import-export-service.ts index 6830c32..b304d8d 100644 --- a/src/services/package-manager/batch-import-export-service.ts +++ b/src/services/package-manager/batch-import-export-service.ts @@ -10,7 +10,7 @@ import { } from "../../interfaces/package-export-transport"; import {FileService, fileService} from "../file-service"; import {studioService} from "../studio/studio.service"; -import {parse, stringify} from "../../util/yaml" +import {parse, stringify} from "../../util/json" import * as FormData from "form-data"; import {BatchExportImportConstants} from "../../interfaces/batch-export-import-constants"; import {packageApi} from "../../api/package-api"; diff --git a/src/services/profile.service.ts b/src/services/profile.service.ts index 2d163f7..a9f6249 100644 --- a/src/services/profile.service.ts +++ b/src/services/profile.service.ts @@ -1,11 +1,20 @@ -import { AuthenticationType, Profile } from "../interfaces/profile.interface"; +import { + AuthenticationType, ClientAuthenticationMethod, + Profile, ProfileType +} from "../interfaces/profile.interface"; import { ProfileValidator } from "../validators/profile.validator"; import * as path from "path"; import * as fs from "fs"; import { FatalError, logger } from "../util/logger"; - +import { Issuer } from "openid-client"; +import axios from "axios"; import os = require("os"); + const homedir = os.homedir(); +// use 5 seconds buffer to avoid rare cases when accessToken is just about to expire before the command is sent +const expiryBuffer = 5000; +const deviceCodeScopes = ["studio", "package-manager", "integration.data-pools", "action-engine.projects"]; +const clientCredentialsScopes = ["studio", "integration.data-pools", "action-engine.projects"]; export interface Config { defaultProfile: string; @@ -25,7 +34,9 @@ export class ProfileService { path.resolve(this.profileContainerPath, this.constructProfileFileName(profileName)), { encoding: "utf-8" } ); - resolve(JSON.parse(file)); + const profile : Profile = JSON.parse(file); + this.refreshProfile(profile) + .then(() => resolve(profile)); } } catch (e) { reject( @@ -75,6 +86,7 @@ export class ProfileService { team: profileVariables.teamUrl, apiToken: profileVariables.apiToken, authenticationType: AuthenticationType.BEARER, + type: ProfileType.KEY }; profile.authenticationType = await ProfileValidator.validateProfile(profile); return profile; @@ -120,6 +132,126 @@ export class ProfileService { return fileNames; } + public async authorizeProfile(profile: Profile) : Promise { + switch (profile.type) { + case ProfileType.KEY: + const url = profile.team.replace(/\/?$/, "/api/cloud/team"); + try { + await this.tryKeyAuthentication(url, AuthenticationType.BEARER, profile.apiToken); + profile.authenticationType = AuthenticationType.BEARER; + } catch (e) { + try { + await this.tryKeyAuthentication(url, AuthenticationType.APPKEY, profile.apiToken); + profile.authenticationType = AuthenticationType.APPKEY; + } catch (err) { + logger.error(new FatalError("The provided team or api key is wrong.")); + } + } + break; + case ProfileType.DEVICE_CODE: + try { + const deviceCodeIssuer = await Issuer.discover(profile.team); + const deviceCodeOAuthClient = new deviceCodeIssuer.Client({ + client_id: "content-cli", + token_endpoint_auth_method: "none", + }); + const deviceCodeHandle = await deviceCodeOAuthClient.deviceAuthorization({ + scope: deviceCodeScopes.join(" ") + }); + logger.info(`Continue authorization here: ${deviceCodeHandle.verification_uri_complete}`); + const deviceCodeTokenSet = await deviceCodeHandle.poll(); + profile.apiToken = deviceCodeTokenSet.access_token; + profile.refreshToken = deviceCodeTokenSet.refresh_token; + profile.expiresAt = deviceCodeTokenSet.expires_at; + } catch (err) { + logger.error(new FatalError("The provided team is wrong.")); + } + break; + case ProfileType.CLIENT_CREDENTIALS: + const clientCredentialsIssuer = await Issuer.discover(profile.team); + try { + // try with client secret basic + const clientCredentialsOAuthClient = new clientCredentialsIssuer.Client({ + client_id: profile.clientId, + client_secret: profile.clientSecret, + token_endpoint_auth_method: ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + }); + const clientCredentialsTokenSet = await clientCredentialsOAuthClient.grant({ + grant_type: "client_credentials", + scope: clientCredentialsScopes.join(" ") + }); + profile.clientAuthenticationMethod = ClientAuthenticationMethod.CLIENT_SECRET_BASIC; + profile.apiToken = clientCredentialsTokenSet.access_token; + profile.expiresAt = clientCredentialsTokenSet.expires_at; + } catch (e) { + try { + // try with client secret post + const clientCredentialsOAuthClient = new clientCredentialsIssuer.Client({ + client_id: profile.clientId, + client_secret: profile.clientSecret, + token_endpoint_auth_method: ClientAuthenticationMethod.CLIENT_SECRET_POST, + }); + const clientCredentialsTokenSet = await clientCredentialsOAuthClient.grant({ + grant_type: "client_credentials", + scope: clientCredentialsScopes.join(" ") + }); + profile.clientAuthenticationMethod = ClientAuthenticationMethod.CLIENT_SECRET_POST; + profile.apiToken = clientCredentialsTokenSet.access_token; + profile.expiresAt = clientCredentialsTokenSet.expires_at; + } catch (err) { + logger.error(new FatalError("The OAuth client configuration is incorrect. " + + "Check the id, secret and scopes for correctness.")); + } + } + profile.scopes = [...clientCredentialsScopes]; + + break; + default: + logger.error(new FatalError("Unsupported profile type")); + break; + } + } + + public async refreshProfile(profile: Profile) : Promise { + if (!this.isProfileExpired(profile, expiryBuffer)) { + return; + } + const issuer = await Issuer.discover(profile.team); + if (profile.type === ProfileType.DEVICE_CODE) { + try { + const oauthClient = new issuer.Client({ + client_id: "content-cli", + token_endpoint_auth_method: "none", + }); + const tokenSet = await oauthClient.refresh(profile.refreshToken); + profile.apiToken = tokenSet.access_token; + profile.expiresAt = tokenSet.expires_at; + profile.refreshToken = tokenSet.refresh_token; + } catch (err) { + logger.error(new FatalError("The profile cannot be refreshed. Please retry or recreate profile.")); + } + } + else { + try { + const oauthClient = new issuer.Client({ + client_id: profile.clientId, + client_secret: profile.clientSecret, + token_endpoint_auth_method: profile.clientAuthenticationMethod, + }); + const tokenSet = await oauthClient.grant({ + grant_type: "client_credentials", + scope: profile.scopes.join(" ") + }); + profile.apiToken = tokenSet.access_token; + profile.expiresAt = tokenSet.expires_at; + } catch (err) { + logger.error(new FatalError("The profile cannot be refreshed. Please retry or recreate profile.")); + } + } + + this.storeProfile(profile); + } + private getProfileEnvVariables(): any { return { teamUrl: this.getBaseTeamUrl(process.env.TEAM_URL), @@ -135,6 +267,34 @@ export class ProfileService { const url = new URL(teamUrl); return url.origin; } + + private isProfileExpired(profile: Profile, buffer: number = 0): boolean { + if (profile.type === null || profile.type === undefined || profile.type === ProfileType.KEY) { + return false; + } + const now = new Date(); + const expirationTime = new Date(profile.expiresAt * 1000 - buffer); + + return now > expirationTime; + } + + private tryKeyAuthentication(url: string, authType: AuthenticationType, apiToken: string): Promise { + return new Promise((resolve, reject) => { + axios.get(url, { + headers: { + Authorization: `${authType} ${apiToken}` + } + }).then(response => { + if (response.status === 200 && response.data.domain) { + resolve(); + } else { + reject(); + } + }).catch(() => { + reject(); + }) + }) + } } export const profileService = new ProfileService(); diff --git a/src/services/studio/studio.service.ts b/src/services/studio/studio.service.ts index 61a5458..83b659b 100644 --- a/src/services/studio/studio.service.ts +++ b/src/services/studio/studio.service.ts @@ -1,11 +1,11 @@ import { + NodeConfiguration, NodeExportTransport, - NodeSerializedContent, PackageExportTransport, PackageKeyAndVersionPair, StudioPackageManifest, VariableExportTransport, - VariableManifestTransport + VariableManifestTransport, } from "../../interfaces/package-export-transport"; import {packageApi} from "../../api/package-api"; @@ -17,7 +17,7 @@ import { } from "../../interfaces/package-manager.interfaces"; import {dataModelService} from "../package-manager/datamodel-service"; import {IZipEntry} from "adm-zip"; -import {parse, stringify} from "../../util/yaml"; +import {parse, stringify} from "../../util/json"; import {nodeApi} from "../../api/node-api"; import {variablesApi} from "../../api/variables-api"; import {spaceApi} from "../../api/space-api"; @@ -140,7 +140,7 @@ class StudioService { } private deleteScenarioAssets(packageZip: AdmZip): void { - packageZip.getEntries().filter(entry => entry.entryName.startsWith(BatchExportImportConstants.NODES_FOLDER_NAME) && entry.entryName.endsWith(BatchExportImportConstants.YAML_EXTENSION)) + packageZip.getEntries().filter(entry => entry.entryName.startsWith(BatchExportImportConstants.NODES_FOLDER_NAME) && entry.entryName.endsWith(BatchExportImportConstants.JSON_EXTENSION)) .forEach(entry => { const node: NodeExportTransport = parse(entry.getData().toString()); if (node.type === BatchExportImportConstants.SCENARIO_NODE) { @@ -157,10 +157,10 @@ class StudioService { return; } - const packageEntry = packageZip.getEntry("package.yml"); + const packageEntry = packageZip.getEntry("package.json"); const exportedNode: NodeExportTransport = parse(packageEntry.getData().toString()); - const nodeContent: NodeSerializedContent = parse(exportedNode.configuration); + const nodeContent: NodeConfiguration = exportedNode.configuration; nodeContent.variables = nodeContent.variables.map(variable => ({ ...variable, @@ -168,7 +168,6 @@ class StudioService { connectionVariablesByKey.get(variable.key).metadata : variable.metadata })); - exportedNode.configuration = stringify(nodeContent); packageZip.updateFile(packageEntry, Buffer.from(stringify(exportedNode))); } @@ -212,7 +211,7 @@ class StudioService { const packageZip = new AdmZip(file.getData()); packageZip.getEntries().forEach(nodeFile => { - if (nodeFile.entryName.endsWith(BatchExportImportConstants.YAML_EXTENSION)) { + if (nodeFile.entryName.endsWith(BatchExportImportConstants.JSON_EXTENSION)) { const updatedNodeFile = this.updateSpaceIdForNode(nodeFile.getData().toString(), spaceId); packageZip.updateFile(nodeFile, Buffer.from(updatedNodeFile)); } diff --git a/src/util/json.ts b/src/util/json.ts new file mode 100644 index 0000000..7f45a68 --- /dev/null +++ b/src/util/json.ts @@ -0,0 +1,15 @@ + +export function stringify(data: any): string { + return JSON.stringify(data, replacer, 2); +} + +export function parse(data: string): T { + return JSON.parse(data); +} + +const replacer = (key, value) => { + if (value instanceof Map) { + return Object.fromEntries(value); + } + return value; +}; diff --git a/src/validators/profile.validator.ts b/src/validators/profile.validator.ts index b77ebd6..b311ae9 100644 --- a/src/validators/profile.validator.ts +++ b/src/validators/profile.validator.ts @@ -1,53 +1,23 @@ -import { AuthenticationType, Profile } from "../interfaces/profile.interface"; +import {Profile, ProfileType} from "../interfaces/profile.interface"; import { FatalError, logger } from "../util/logger"; import validUrl = require("valid-url"); -import axios from "axios"; export class ProfileValidator { public static async validateProfile(profile: Profile): Promise { - return new Promise(async (resolve, reject) => { - if (profile.name == null) { - logger.error(new FatalError("The name can not be empty")); - } - if (profile.team == null) { - logger.error(new FatalError("The team can not be empty")); - } - if (profile.apiToken == null) { - logger.error(new FatalError("The api token can not be empty")); - } - if (!validUrl.isUri(profile.team)) { - logger.error(new FatalError("The provided url is not a valid url.")); - } - const url = profile.team.replace(/\/?$/, "/api/cloud/team"); - - this.tryAuthenticationType(url, AuthenticationType.BEARER, profile.apiToken).then(() => { - resolve(AuthenticationType.BEARER); - }).catch(() => { - this.tryAuthenticationType(url, AuthenticationType.APPKEY, profile.apiToken).then(() => { - resolve(AuthenticationType.APPKEY); - }).catch(() => { - logger.error(new FatalError("The provided team or api key is wrong.")); - reject(); - }) - }); - }); - } - - private static tryAuthenticationType(url: string, authType: AuthenticationType, apiToken: string): Promise { - return new Promise((resolve, reject) => { - axios.get(url, { - headers: { - Authorization: `${authType} ${apiToken}` - } - }).then(response => { - if (response.status === 200 && response.data.domain) { - resolve(); - } else { - reject(); - } - }).catch(() => { - reject(); - }) - }) + if (profile.name == null) { + logger.error(new FatalError("The name can not be empty")); + } + if (profile.team == null) { + logger.error(new FatalError("The team can not be empty")); + } + if (profile.type === ProfileType.KEY && profile.apiToken == null) { + logger.error(new FatalError("The api token can not be empty for this profile type")); + } + if (profile.type === ProfileType.CLIENT_CREDENTIALS && (profile.clientId == null || profile.clientSecret == null)) { + logger.error(new FatalError("The client id and secret can not be empty for this profile type")); + } + if (!validUrl.isUri(profile.team)) { + logger.error(new FatalError("The provided url is not a valid url.")); + } } } diff --git a/tests/config/config-diff.spec.ts b/tests/config/config-diff.spec.ts index 3e9aec4..8504fe1 100644 --- a/tests/config/config-diff.spec.ts +++ b/tests/config/config-diff.spec.ts @@ -1,7 +1,6 @@ import {PackageManifestTransport} from "../../src/interfaces/package-export-transport"; import {ConfigUtils} from "../utls/config-utils"; import * as path from "path"; -import {stringify} from "../../src/util/yaml"; import {mockCreateReadStream, mockExistsSync, mockReadFileSync} from "../utls/fs-mock-utils"; import { PackageDiffMetadata, @@ -22,7 +21,7 @@ describe("Config diff", () => { const manifest: PackageManifestTransport[] = []; manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); - const firstPackageNode = ConfigUtils.buildPackageNode("package-key", stringify({metadata: {description: "test"}, variables: [], dependencies: []})); + const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); @@ -49,7 +48,7 @@ describe("Config diff", () => { const manifest: PackageManifestTransport[] = []; manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); - const firstPackageNode = ConfigUtils.buildPackageNode("package-key", stringify({metadata: {description: "test"}, variables: [], dependencies: []})); + const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); @@ -91,7 +90,7 @@ describe("Config diff", () => { const manifest: PackageManifestTransport[] = []; manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); - const firstPackageNode = ConfigUtils.buildPackageNode("package-key", stringify({metadata: {description: "test"}, variables: [], dependencies: []})); + const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); @@ -137,7 +136,7 @@ describe("Config diff", () => { const manifest: PackageManifestTransport[] = []; manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); - const firstPackageNode = ConfigUtils.buildPackageNode("package-key", stringify({metadata: {description: "test"}, variables: [], dependencies: []})); + const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); diff --git a/tests/config/config-export.spec.ts b/tests/config/config-export.spec.ts index 85612d5..a6d520a 100644 --- a/tests/config/config-export.spec.ts +++ b/tests/config/config-export.spec.ts @@ -1,9 +1,9 @@ import {ConfigUtils} from "../utls/config-utils"; import { - DependencyTransport, NodeExportTransport, NodeSerializedContent, + DependencyTransport, NodeConfiguration, NodeExportTransport, PackageManifestTransport, StudioPackageManifest, - VariableManifestTransport + VariableManifestTransport, } from "../../src/interfaces/package-export-transport"; import {mockAxiosGet, mockAxiosPost, mockedPostRequestBodyByUrl} from "../utls/http-requests-mock"; import {ConfigCommand} from "../../src/commands/config.command"; @@ -13,7 +13,7 @@ import {FileService} from "../../src/services/file-service"; import * as fs from "fs"; import AdmZip = require("adm-zip"); -import { parse, stringify } from "../../src/util/yaml"; +import { parse, stringify } from "../../src/util/json"; import { PackageManagerVariableType, VariableDefinition, @@ -109,7 +109,7 @@ describe("Config export", () => { } ]; - const firstPackageNode = ConfigUtils.buildPackageNode("key-1", stringify({variables: firstPackageVariableDefinition})); + const firstPackageNode = ConfigUtils.buildPackageNode("key-1", {variables: firstPackageVariableDefinition}); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); const secondPackageVariableDefinition: VariableDefinition[] = [ @@ -125,7 +125,7 @@ describe("Config export", () => { } ]; - const secondPackageNode = ConfigUtils.buildPackageNode("key-2", stringify({variables: secondPackageVariableDefinition})); + const secondPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: secondPackageVariableDefinition}); const secondPackageZip = ConfigUtils.buildExportPackageZip(secondPackageNode, [], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip, secondPackageZip]); @@ -258,12 +258,12 @@ describe("Config export", () => { manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", BatchExportImportConstants.STUDIO)); manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", BatchExportImportConstants.STUDIO)); - const firstPackageNode = ConfigUtils.buildPackageNode("key-1", ""); + const firstPackageNode = ConfigUtils.buildPackageNode("key-1", {}); const firstPackageScenarioChild = ConfigUtils.buildChildNode("child-1-scenario", firstPackageNode.key, "SCENARIO"); const firstPackageTestChild = ConfigUtils.buildChildNode("child-2", firstPackageNode.key, "TEST"); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstPackageScenarioChild, firstPackageTestChild], "1.0.0"); - const secondPackageNode = ConfigUtils.buildPackageNode("key-2", ""); + const secondPackageNode = ConfigUtils.buildPackageNode("key-2", {}); const secondPackageScenarioChild = ConfigUtils.buildChildNode("child-3-scenario", secondPackageNode.key, "SCENARIO"); const secondPackageTestChild = ConfigUtils.buildChildNode("child-4", secondPackageNode.key, "TEST"); const secondPackageZip = ConfigUtils.buildExportPackageZip(secondPackageNode, [secondPackageScenarioChild, secondPackageTestChild], "1.0.0"); @@ -287,15 +287,15 @@ describe("Config export", () => { const actualZip = new AdmZip(fileBuffer); const firstPackageExportedZip = new AdmZip(actualZip.getEntry("key-1_1.0.0.zip").getData()); - expect(firstPackageExportedZip.getEntry("nodes/child-1-scenario.yml")).toBeNull(); - expect(firstPackageExportedZip.getEntry("nodes/child-2.yml").getData().toString()).toEqual(stringify(firstPackageTestChild)); + expect(firstPackageExportedZip.getEntry("nodes/child-1-scenario.json")).toBeNull(); + expect(firstPackageExportedZip.getEntry("nodes/child-2.json").getData().toString()).toEqual(stringify(firstPackageTestChild)); const secondPackageExportedZip = new AdmZip(actualZip.getEntry("key-2_1.0.0.zip").getData()); - expect(secondPackageExportedZip.getEntry("nodes/child-3-scenario.yml")).toBeNull(); - expect(secondPackageExportedZip.getEntry("nodes/child-4.yml").getData().toString()).toEqual(stringify(secondPackageTestChild)); + expect(secondPackageExportedZip.getEntry("nodes/child-3-scenario.json")).toBeNull(); + expect(secondPackageExportedZip.getEntry("nodes/child-4.json").getData().toString()).toEqual(stringify(secondPackageTestChild)); }) - it("Should add appName to metadata for CONNECTION variables of package.yml files", async () => { + it("Should add appName to metadata for CONNECTION variables of package.json files", async () => { const manifest: PackageManifestTransport[] = []; manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", BatchExportImportConstants.STUDIO)); manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", BatchExportImportConstants.STUDIO)); @@ -313,7 +313,7 @@ describe("Config export", () => { } ]; - const firstPackageNode = ConfigUtils.buildPackageNode("key-1", stringify({variables: firstPackageVariableDefinition})); + const firstPackageNode = ConfigUtils.buildPackageNode("key-1", {variables: firstPackageVariableDefinition}); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); const secondPackageVariableDefinition: VariableDefinition[] = [ @@ -332,7 +332,7 @@ describe("Config export", () => { } ]; - const secondPackageNode = ConfigUtils.buildPackageNode("key-2", stringify({variables: secondPackageVariableDefinition})); + const secondPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: secondPackageVariableDefinition}); const secondPackageZip = ConfigUtils.buildExportPackageZip(secondPackageNode, [], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip, secondPackageZip]); @@ -403,9 +403,9 @@ describe("Config export", () => { const actualZip = new AdmZip(fileBuffer); const firstPackageExportedZip = new AdmZip(actualZip.getEntry("key-1_1.0.0.zip").getData()); - const firstPackageExportedNode: NodeExportTransport = parse(firstPackageExportedZip.getEntry("package.yml").getData().toString()); + const firstPackageExportedNode: NodeExportTransport = parse(firstPackageExportedZip.getEntry("package.json").getData().toString()); expect(firstPackageExportedNode).toBeTruthy(); - const firstPackageContent: NodeSerializedContent = parse(firstPackageExportedNode.configuration); + const firstPackageContent: NodeConfiguration = firstPackageExportedNode.configuration; expect(firstPackageContent.variables).toHaveLength(2); expect(firstPackageContent.variables).toEqual([ { @@ -420,9 +420,9 @@ describe("Config export", () => { ]); const secondPackageExportedZip = new AdmZip(actualZip.getEntry("key-2_1.0.0.zip").getData()); - const secondPackageExportedNode: NodeExportTransport = parse(secondPackageExportedZip.getEntry("package.yml").getData().toString()); + const secondPackageExportedNode: NodeExportTransport = parse(secondPackageExportedZip.getEntry("package.json").getData().toString()); expect(secondPackageExportedNode).toBeTruthy(); - const secondPackageContent: NodeSerializedContent = parse(secondPackageExportedNode.configuration); + const secondPackageContent: NodeConfiguration = secondPackageExportedNode.configuration; expect(secondPackageContent.variables).toHaveLength(2); expect(secondPackageContent.variables).toEqual([{ ...secondPackageVariableDefinition[0], @@ -458,7 +458,7 @@ describe("Config export", () => { } ]; - const firstPackageNode = ConfigUtils.buildPackageNode("key_with_underscores_1", stringify({variables: firstPackageVariableDefinition})); + const firstPackageNode = ConfigUtils.buildPackageNode("key_with_underscores_1", {variables: firstPackageVariableDefinition}); const firstPackageScenarioChild = ConfigUtils.buildChildNode("child-1-scenario", firstPackageNode.key, "SCENARIO"); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstPackageScenarioChild], "1.0.0"); @@ -511,9 +511,9 @@ describe("Config export", () => { const actualZip = new AdmZip(fileBuffer); const firstPackageExportedZip = new AdmZip(actualZip.getEntry("key_with_underscores_1_1.0.0.zip").getData()); - const firstPackageExportedNode: NodeExportTransport = parse(firstPackageExportedZip.getEntry("package.yml").getData().toString()); + const firstPackageExportedNode: NodeExportTransport = parse(firstPackageExportedZip.getEntry("package.json").getData().toString()); expect(firstPackageExportedNode).toBeTruthy(); - const firstPackageContent: NodeSerializedContent = parse(firstPackageExportedNode.configuration); + const firstPackageContent: NodeConfiguration = firstPackageExportedNode.configuration; expect(firstPackageContent.variables).toHaveLength(3); expect(firstPackageContent.variables).toEqual([ { @@ -532,7 +532,7 @@ describe("Config export", () => { } ]); - expect(firstPackageExportedZip.getEntry("nodes/child-1-scenario.yml")).toBeNull(); + expect(firstPackageExportedZip.getEntry("nodes/child-1-scenario.json")).toBeNull(); }) it("Should export by packageKeys without dependencies", async () => { diff --git a/tests/config/config-import.spec.ts b/tests/config/config-import.spec.ts index 2cde1bb..e16c964 100644 --- a/tests/config/config-import.spec.ts +++ b/tests/config/config-import.spec.ts @@ -12,7 +12,7 @@ import { import {ConfigUtils} from "../utls/config-utils"; import {mockWriteFileSync, testTransport} from "../jest.setup"; import * as path from "path"; -import {stringify} from "../../src/util/yaml"; +import {stringify} from "../../src/util/json"; import {SpaceTransport} from "../../src/interfaces/save-space.interface"; import {packageApi} from "../../src/api/package-api"; import { @@ -68,7 +68,7 @@ describe("Config import", () => { const studioManifest: StudioPackageManifest[] = []; studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "spaceName", "space-id")); - const firstPackageNode = ConfigUtils.buildPackageNode("key-2", stringify({variables: []})); + const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); @@ -112,7 +112,7 @@ describe("Config import", () => { const studioManifest: StudioPackageManifest[] = []; studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "spaceName", "space")); - const firstPackageNode = ConfigUtils.buildPackageNode("key-2", stringify({variables: []})); + const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); @@ -139,7 +139,7 @@ describe("Config import", () => { const studioManifest: StudioPackageManifest[] = []; studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "space", "spaceId")); - const firstPackageNode = ConfigUtils.buildPackageNode("key-2", stringify({variables: []})); + const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); @@ -186,7 +186,7 @@ describe("Config import", () => { const studioManifest: StudioPackageManifest[] = []; studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "spaceName", null)); - const firstPackageNode = ConfigUtils.buildPackageNode("key-2", stringify({variables: []})); + const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); @@ -225,7 +225,7 @@ describe("Config import", () => { const studioManifest: StudioPackageManifest[] = []; studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "otherName", null)); - const firstPackageNode = ConfigUtils.buildPackageNode("key-2", stringify({variables: []})); + const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); diff --git a/tests/config/config-list-variables.spec.ts b/tests/config/config-list-variables.spec.ts index 45f479e..a63c87b 100644 --- a/tests/config/config-list-variables.spec.ts +++ b/tests/config/config-list-variables.spec.ts @@ -5,7 +5,7 @@ import * as path from "path"; import {PackageKeyAndVersionPair, VariableManifestTransport} from "../../src/interfaces/package-export-transport"; import {PackageManagerVariableType} from "../../src/interfaces/package-manager.interfaces"; import {mockAxiosPost, mockedPostRequestBodyByUrl} from "../utls/http-requests-mock"; -import {parse} from "../../src/util/yaml"; +import {parse} from "../../src/util/json"; import * as fs from "fs"; describe("Config listVariables", () => { diff --git a/tests/utls/config-utils.ts b/tests/utls/config-utils.ts index 0ab9683..e796278 100644 --- a/tests/utls/config-utils.ts +++ b/tests/utls/config-utils.ts @@ -1,10 +1,10 @@ import AdmZip = require("adm-zip"); import { - DependencyTransport, + DependencyTransport, NodeConfiguration, NodeExportTransport, - PackageManifestTransport, StudioPackageManifest + PackageManifestTransport, StudioPackageManifest, } from "../../src/interfaces/package-export-transport"; -import {stringify} from "../../src/util/yaml"; +import {stringify} from "../../src/util/json"; import {SpaceTransport} from "../../src/interfaces/save-space.interface"; export class ConfigUtils { @@ -12,8 +12,8 @@ export class ConfigUtils { public static buildBatchExportZipWithStudioManifest(manifest: PackageManifestTransport[], studioManifest: StudioPackageManifest[], packageZips: AdmZip[]): AdmZip { const zipExport = new AdmZip(); - zipExport.addFile("manifest.yml", Buffer.from(stringify(manifest))); - zipExport.addFile("studio.yml", Buffer.from(stringify(studioManifest))); + zipExport.addFile("manifest.json", Buffer.from(stringify(manifest))); + zipExport.addFile("studio.json", Buffer.from(stringify(studioManifest))); packageZips.forEach(packageZip => { const fileName = `${packageZip.getZipComment()}.zip` packageZip.addZipComment("") @@ -25,7 +25,7 @@ export class ConfigUtils { public static buildBatchExportZip(manifest: PackageManifestTransport[], packageZips: AdmZip[]): AdmZip { const zipExport = new AdmZip(); - zipExport.addFile("manifest.yml", Buffer.from(stringify(manifest))); + zipExport.addFile("manifest.json", Buffer.from(stringify(manifest))); packageZips.forEach(packageZip => { const fileName = `${packageZip.getZipComment()}.zip` packageZip.addZipComment("") @@ -38,11 +38,11 @@ export class ConfigUtils { public static buildExportPackageZip(packageNode: NodeExportTransport, childNodes: NodeExportTransport[], version: string): AdmZip { const zipExport = new AdmZip(); - zipExport.addFile("package.yml", Buffer.from(stringify(packageNode))); + zipExport.addFile("package.json", Buffer.from(stringify(packageNode))); zipExport.addFile("nodes/", Buffer.alloc(0)); childNodes.forEach(child => { - zipExport.addFile(`nodes/${child.key}.yml`, Buffer.from(stringify(child))); + zipExport.addFile(`nodes/${child.key}.json`, Buffer.from(stringify(child))); }); zipExport.addZipComment(`${packageNode.key}_${version}`); @@ -59,7 +59,7 @@ export class ConfigUtils { }; } - public static buildPackageNode(key: string, configuration: string): NodeExportTransport { + public static buildPackageNode(key: string, configuration: NodeConfiguration): NodeExportTransport { return { key, parentNodeKey: key, @@ -81,7 +81,7 @@ export class ConfigUtils { name: "name", type: type, exportSerializationType: "YAML", - configuration: "", + configuration: {}, schemaVersion: 1, invalidContent: false, serializedDocument: null, diff --git a/tests/utls/context-mock.ts b/tests/utls/context-mock.ts index f1e239e..378d327 100644 --- a/tests/utls/context-mock.ts +++ b/tests/utls/context-mock.ts @@ -4,9 +4,10 @@ export function setDefaultProfile(): void { contextService.setContext({ profile: { name: "test", + type: "Key", team: "https://myTeam.celonis.cloud/", apiToken: "YnQ3N2M0M2ItYzQ3OS00YzgyLTg0ODgtOWNkNzhiNzYwOTU2OlFkNnBpVCs0M0JBYm1ZWGlCZ2hPd245aldwWTNubFQyYVFOTFBUeHEwdUxM", authenticationType: "Bearer" } }); -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 321ade0..b1f93b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2816,6 +2816,11 @@ jmespath@0.15.0: resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= +jose@^4.15.1: + version "4.15.4" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.4.tgz#02a9a763803e3872cf55f29ecef0dfdcc218cc03" + integrity sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -3190,6 +3195,16 @@ object-assign@^4.1.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +object-hash@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== + +oidc-token-hash@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz#9a229f0a1ce9d4fc89bcaee5478c97a889e7b7b6" + integrity sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3223,6 +3238,16 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +openid-client@^5.6.1: + version "5.6.1" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.6.1.tgz#8f7526a50c290a5e28a7fe21b3ece3107511bc73" + integrity sha512-PtrWsY+dXg6y8mtMPyL/namZSYVz8pjXz3yJiBNZsEdCnu9miHLB4ELVC85WvneMKo2Rg62Ay7NkuCpM0bgiLQ== + dependencies: + jose "^4.15.1" + lru-cache "^6.0.0" + object-hash "^2.2.0" + oidc-token-hash "^5.0.3" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"