From de313a1c9490de15b9ad1116222da74d5629a8ba Mon Sep 17 00:00:00 2001 From: erdoganoksuz Date: Wed, 24 Feb 2021 12:00:29 +0300 Subject: [PATCH 01/17] swift publisher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- .../src/stages/publish/openStackSwift.ts | 15 ++++- .../src/stages/publish/publish.ts | 6 ++ .../src/stages/publish/types.ts | 3 +- plugins/techdocs-backend/config.d.ts | 2 +- plugins/techdocs/config.d.ts | 56 +++++++++++++++++++ 5 files changed, 77 insertions(+), 5 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts index 3331033f66..b174122789 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -103,16 +103,19 @@ export class OpenStackSwiftPublish implements PublisherBase { this.logger = logger; } + public myName: string = 'hey'; + /** - * Upload all the files from the generated `directory` to the S3 bucket. + * Upload all the files from the generated `directory` to the OpenStack Swift container. * Directory structure used in the bucket is - entityNamespace/entityKind/entityName/index.html */ async publish({ entity, directory }: PublishRequest): Promise { + console.log(entity, directory, 'Publish hey'); try { - // Note: S3 manages creation of parent directories if they do not exist. + // Note: OpenStack Swift manages creation of parent directories if they do not exist. // So collecting path of only the files is good enough. const allFilesToUpload = await getFileTreeRecursively(directory); - + console.log(allFilesToUpload, entity, 'hey'); const limiter = createLimiter(10); const uploadPromises: Array> = []; for (const filePath of allFilesToUpload) { @@ -169,6 +172,8 @@ export class OpenStackSwiftPublish implements PublisherBase { async fetchTechDocsMetadata( entityName: EntityName, ): Promise { + console.log(entityName, 'fetchTechDocsMetadata hey'); + try { return await new Promise(async (resolve, reject) => { const entityRootDir = `${entityName.namespace}/${entityName.kind}/${entityName.name}`; @@ -208,6 +213,8 @@ export class OpenStackSwiftPublish implements PublisherBase { return async (req, res) => { // Trim the leading forward slash // filePath example - /default/Component/documented-component/index.html + console.log('docsRouter hey'); + const filePath = req.path.replace(/^\//, ''); // Files with different extensions (CSS, HTML) need to be served with different headers @@ -241,6 +248,8 @@ export class OpenStackSwiftPublish implements PublisherBase { */ async hasDocsBeenGenerated(entity: Entity): Promise { try { + console.log(entity, 'hasDocsBeenGenerated hey'); + const entityRootDir = `${entity.metadata.namespace}/${entity.kind}/${entity.metadata.name}`; this.storageClient.getFile( this.containerName, diff --git a/packages/techdocs-common/src/stages/publish/publish.ts b/packages/techdocs-common/src/stages/publish/publish.ts index a4e33a2d9c..f70a5f7626 100644 --- a/packages/techdocs-common/src/stages/publish/publish.ts +++ b/packages/techdocs-common/src/stages/publish/publish.ts @@ -22,6 +22,7 @@ import { LocalPublish } from './local'; import { GoogleGCSPublish } from './googleStorage'; import { AwsS3Publish } from './awsS3'; import { AzureBlobStoragePublish } from './azureBlobStorage'; +import { OpenStackSwiftPublish } from './openStackSwift'; type factoryOptions = { logger: Logger; @@ -53,6 +54,11 @@ export class Publisher { 'Creating Azure Blob Storage Container publisher for TechDocs', ); return AzureBlobStoragePublish.fromConfig(config, logger); + case 'openStackSwift': + logger.info( + 'Creating OpenStack Swift Container publisher for TechDocs', + ); + return OpenStackSwiftPublish.fromConfig(config, logger); case 'local': logger.info('Creating Local publisher for TechDocs'); return new LocalPublish(config, logger, discovery); diff --git a/packages/techdocs-common/src/stages/publish/types.ts b/packages/techdocs-common/src/stages/publish/types.ts index 5e953deb81..f0ecb3cb2b 100644 --- a/packages/techdocs-common/src/stages/publish/types.ts +++ b/packages/techdocs-common/src/stages/publish/types.ts @@ -23,7 +23,8 @@ export type PublisherType = | 'local' | 'googleGcs' | 'awsS3' - | 'azureBlobStorage'; + | 'azureBlobStorage' + | 'openStackSwift'; export type PublishRequest = { entity: Entity; diff --git a/plugins/techdocs-backend/config.d.ts b/plugins/techdocs-backend/config.d.ts index 5a323f244a..c704014b14 100644 --- a/plugins/techdocs-backend/config.d.ts +++ b/plugins/techdocs-backend/config.d.ts @@ -33,7 +33,7 @@ export interface Config { * Techdocs publisher information */ publisher: { - type: 'local' | 'googleGcs' | 'awsS3'; + type: 'local' | 'googleGcs' | 'awsS3' | 'openStackSwift'; }; /** diff --git a/plugins/techdocs/config.d.ts b/plugins/techdocs/config.d.ts index 1c49833d68..c4fe4a47db 100644 --- a/plugins/techdocs/config.d.ts +++ b/plugins/techdocs/config.d.ts @@ -85,6 +85,62 @@ export interface Config { region?: string; }; } + | { + type: 'openStackSwift'; + + /** + * Required when 'type' is set to awsS3 + */ + openStackSwift?: { + /** + * (Optional) Credentials used to access a storage bucket. + * If not set, environment variables or aws config file will be used to authenticate. + * @see https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/loading-node-credentials-environment.html + * @see https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/loading-node-credentials-shared.html + * @visibility secret + */ + /** + * (Required) Cloud Storage Container Name + * @visibility backend + */ + containerName: string; + /** + * (Required) Root user name + * @visibility backend + */ + username: string; + /** + * (Required) Root user password + * @visibility backend + */ + password: string; // required + /** + * (Required) Auth url sometimes OpenStack uses different port check your OpenStack apis. + * @visibility backend + */ + authUrl: string; + /** + * (Required) Auth version + * @visibility backend + */ + keystoneAuthVersion: string; + /** + * (Required) Domaind Id + * @visibility backend + */ + domainId: string; + /** + * (Required) Domaind Name + * @visibility backend + */ + domainName: 'Default'; + /** + * (Required) Region + * @visibility backend + */ + region: 'earth'; + }; + } | { type: 'azureBlobStorage'; From 0d3f211a24418d6e7d75f41b60e9a1ca43aa92bd Mon Sep 17 00:00:00 2001 From: erdoganoksuz Date: Wed, 24 Feb 2021 12:10:29 +0300 Subject: [PATCH 02/17] console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- .../techdocs-common/src/stages/publish/openStackSwift.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts index b174122789..49d7aaee23 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -110,12 +110,10 @@ export class OpenStackSwiftPublish implements PublisherBase { * Directory structure used in the bucket is - entityNamespace/entityKind/entityName/index.html */ async publish({ entity, directory }: PublishRequest): Promise { - console.log(entity, directory, 'Publish hey'); try { // Note: OpenStack Swift manages creation of parent directories if they do not exist. // So collecting path of only the files is good enough. const allFilesToUpload = await getFileTreeRecursively(directory); - console.log(allFilesToUpload, entity, 'hey'); const limiter = createLimiter(10); const uploadPromises: Array> = []; for (const filePath of allFilesToUpload) { @@ -172,8 +170,6 @@ export class OpenStackSwiftPublish implements PublisherBase { async fetchTechDocsMetadata( entityName: EntityName, ): Promise { - console.log(entityName, 'fetchTechDocsMetadata hey'); - try { return await new Promise(async (resolve, reject) => { const entityRootDir = `${entityName.namespace}/${entityName.kind}/${entityName.name}`; @@ -213,7 +209,6 @@ export class OpenStackSwiftPublish implements PublisherBase { return async (req, res) => { // Trim the leading forward slash // filePath example - /default/Component/documented-component/index.html - console.log('docsRouter hey'); const filePath = req.path.replace(/^\//, ''); @@ -248,8 +243,6 @@ export class OpenStackSwiftPublish implements PublisherBase { */ async hasDocsBeenGenerated(entity: Entity): Promise { try { - console.log(entity, 'hasDocsBeenGenerated hey'); - const entityRootDir = `${entity.metadata.namespace}/${entity.kind}/${entity.metadata.name}`; this.storageClient.getFile( this.containerName, From a42972e8f3213d17e177e3cc40f8f1b2a4e9dcf1 Mon Sep 17 00:00:00 2001 From: erdoganoksuz Date: Wed, 24 Feb 2021 12:14:14 +0300 Subject: [PATCH 03/17] remove prop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- packages/techdocs-common/src/stages/publish/openStackSwift.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts index 49d7aaee23..066e1e56d2 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -103,8 +103,6 @@ export class OpenStackSwiftPublish implements PublisherBase { this.logger = logger; } - public myName: string = 'hey'; - /** * Upload all the files from the generated `directory` to the OpenStack Swift container. * Directory structure used in the bucket is - entityNamespace/entityKind/entityName/index.html From ed3afb80bc7ee04b66e86e3913479c8bdbbbb6bc Mon Sep 17 00:00:00 2001 From: erdoganoksuz Date: Wed, 24 Feb 2021 17:15:37 +0300 Subject: [PATCH 04/17] tests setup added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- .../techdocs-common/src/stages/publish/openStackSwift.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts index 066e1e56d2..30314e1109 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -15,7 +15,7 @@ */ import { Entity, EntityName } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; -import pkgcloud from 'pkgcloud'; +import { storage } from 'pkgcloud'; import express from 'express'; import fs from 'fs-extra'; import JSON5 from 'json5'; @@ -57,7 +57,7 @@ export class OpenStackSwiftPublish implements PublisherBase { 'techdocs.publisher.openStackSwift', ); - const storageClient = pkgcloud.storage.createClient({ + const storageClient = storage.createClient({ provider: 'openstack', username: openStackSwiftConfig.getString('username'), password: openStackSwiftConfig.getString('password'), @@ -86,7 +86,6 @@ export class OpenStackSwiftPublish implements PublisherBase { ); logger.error(`from OpenStack client library: ${err.message}`); - throw new Error(); } }); @@ -94,7 +93,7 @@ export class OpenStackSwiftPublish implements PublisherBase { } constructor( - private readonly storageClient: pkgcloud.storage.Client, + private readonly storageClient: storage.Client, private readonly containerName: string, private readonly logger: Logger, ) { From e36ef00fd0ee961e386e224362bbf1ae70040602 Mon Sep 17 00:00:00 2001 From: erdoganoksuz Date: Thu, 25 Feb 2021 11:35:39 +0300 Subject: [PATCH 05/17] router case added and hasDocsBeenGenerated fixed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- .../src/stages/publish/openStackSwift.ts | 28 +++++++++++-------- .../techdocs-backend/src/service/router.ts | 1 + 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts index 30314e1109..15d8b20332 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -107,6 +107,7 @@ export class OpenStackSwiftPublish implements PublisherBase { * Directory structure used in the bucket is - entityNamespace/entityKind/entityName/index.html */ async publish({ entity, directory }: PublishRequest): Promise { + this.logger.info(`Publish Called hey`); try { // Note: OpenStack Swift manages creation of parent directories if they do not exist. // So collecting path of only the files is good enough. @@ -167,6 +168,7 @@ export class OpenStackSwiftPublish implements PublisherBase { async fetchTechDocsMetadata( entityName: EntityName, ): Promise { + this.logger.info(`fetchTechDocsMetadata Called hey`); try { return await new Promise(async (resolve, reject) => { const entityRootDir = `${entityName.namespace}/${entityName.kind}/${entityName.name}`; @@ -204,6 +206,7 @@ export class OpenStackSwiftPublish implements PublisherBase { */ docsRouter(): express.Handler { return async (req, res) => { + this.logger.info(`docsRouter Called hey`); // Trim the leading forward slash // filePath example - /default/Component/documented-component/index.html @@ -240,18 +243,21 @@ export class OpenStackSwiftPublish implements PublisherBase { */ async hasDocsBeenGenerated(entity: Entity): Promise { try { + this.logger.info(`hasDocsBeenGenerated Called hey`); const entityRootDir = `${entity.metadata.namespace}/${entity.kind}/${entity.metadata.name}`; - this.storageClient.getFile( - this.containerName, - `${entityRootDir}/index.html`, - (err: any, file: any) => { - if (!err && file) { - return Promise.resolve(true); - } - return Promise.resolve(false); - }, - ); - return Promise.resolve(true); + + return new Promise(res => { + this.storageClient.getFile( + this.containerName, + `${entityRootDir}/index.html`, + (err: any, file: any) => { + console.log(file); + if (!err && file) { + res(true); + } else res(false); + }, + ); + }); } catch (e) { return Promise.resolve(false); } diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index 7f6ff60fdd..b331d2170c 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -155,6 +155,7 @@ export async function createRouter({ break; case 'awsS3': case 'azureBlobStorage': + case 'openStackSwift': case 'googleGcs': // This block should be valid for all external storage implementations. So no need to duplicate in future, // add the publisher type in the list here. From 75ec0e2358fec86f08ef95b8b9ee716be5d50c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Can=20Bilgi=C3=A7?= Date: Thu, 25 Feb 2021 10:48:25 +0300 Subject: [PATCH 06/17] mocked openstackswift publisher and created mocks first test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- .../techdocs-common/__mocks__/pkgcloud.ts | 94 +++++++++++++++++++ .../src/stages/publish/publish.test.ts | 25 +++++ 2 files changed, 119 insertions(+) create mode 100644 packages/techdocs-common/__mocks__/pkgcloud.ts diff --git a/packages/techdocs-common/__mocks__/pkgcloud.ts b/packages/techdocs-common/__mocks__/pkgcloud.ts new file mode 100644 index 0000000000..3ea3c251e0 --- /dev/null +++ b/packages/techdocs-common/__mocks__/pkgcloud.ts @@ -0,0 +1,94 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { OpenstackProviderOptions } from 'pkgcloud'; +import fs from 'fs-extra'; +import os from 'os'; +import path from 'path'; + +const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; + +const checkFileExists = async (Key: string): Promise => { + // Key will always have / as file separator irrespective of OS since S3 expects /. + // Normalize Key to OS specific path before checking if file exists. + const relativeFilePath = Key.split(path.posix.sep).join(path.sep); + const filePath = path.join(rootDir, Key); + + try { + await fs.access(filePath, fs.constants.F_OK); + return true; + } catch (err) { + return false; + } +}; + +class PkgCloudStorageClient { + getFile( + containerName: string, + file: string, + callback: (err: string, file: string) => any, + ) { + checkFileExists(file).then(res => { + if (!res) { + callback('File does not exist', undefined); + throw new Error('File does not exist'); + } else { + callback(undefined, 'success'); + } + }); + } + + getContainer( + containerName: string, + callback: (err: string, container: string) => any, + ) { + if (containerName !== 'mock') { + callback("Container doesn't exist", undefined); + throw new Error('Container does not exist'); + } else { + callback(undefined, 'success'); + } + } + + upload({ containerName, remote }: { containerName: string; remote: string }) { + checkFileExists(remote).then(res => { + if (!res) { + return new Error("File doesn't exists"); + } + return fs.createWriteStream(`${containerName}/${remote}`); + }); + } + + download({ + containerName, + remote, + }: { + containerName: string; + remote: string; + }) { + checkFileExists(remote).then(res => { + if (!res) { + return new Error("File doesn't exists"); + } + return fs.createReadStream(remote); + }); + } +} + +export class storage { + static createClient(params: OpenstackProviderOptions) { + return new PkgCloudStorageClient(); + } +} diff --git a/packages/techdocs-common/src/stages/publish/publish.test.ts b/packages/techdocs-common/src/stages/publish/publish.test.ts index d1faf82b78..f9bd0f3934 100644 --- a/packages/techdocs-common/src/stages/publish/publish.test.ts +++ b/packages/techdocs-common/src/stages/publish/publish.test.ts @@ -23,6 +23,7 @@ import { LocalPublish } from './local'; import { GoogleGCSPublish } from './googleStorage'; import { AwsS3Publish } from './awsS3'; import { AzureBlobStoragePublish } from './azureBlobStorage'; +import { OpenStackSwiftPublish } from './openStackSwift'; const logger = getVoidLogger(); const discovery: jest.Mocked = { @@ -161,4 +162,28 @@ describe('Publisher', () => { }); expect(publisher).toBeInstanceOf(AzureBlobStoragePublish); }); + + it('should create Open Stack Swift publisher from config', async () => { + const mockConfig = new ConfigReader({ + techdocs: { + requestUrl: 'http://localhost:7000', + publisher: { + type: 'openStackSwift', + openStackSwift: { + username: 'mockuser', + password: 'verystrongpass', + authUrl: 'mockauthurl', + region: 'mockregion', + containerName: 'mock', + }, + }, + }, + }); + + const publisher = await Publisher.fromConfig(mockConfig, { + logger, + discovery, + }); + expect(publisher).toBeInstanceOf(OpenStackSwiftPublish); + }); }); From 32fac33d8d8e46b8dc90837f9e75bd589edcf88c Mon Sep 17 00:00:00 2001 From: gmzsenturk Date: Thu, 25 Feb 2021 15:29:23 +0300 Subject: [PATCH 07/17] OpenStackSwift tests added to project MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- .../techdocs-common/__mocks__/pkgcloud.ts | 41 +-- packages/techdocs-common/package.json | 1 + .../src/stages/publish/openStackSwift.test.ts | 248 ++++++++++++++++++ .../src/stages/publish/openStackSwift.ts | 8 +- yarn.lock | 181 +++++++++++-- 5 files changed, 424 insertions(+), 55 deletions(-) create mode 100644 packages/techdocs-common/src/stages/publish/openStackSwift.test.ts diff --git a/packages/techdocs-common/__mocks__/pkgcloud.ts b/packages/techdocs-common/__mocks__/pkgcloud.ts index 3ea3c251e0..bf729a7bd2 100644 --- a/packages/techdocs-common/__mocks__/pkgcloud.ts +++ b/packages/techdocs-common/__mocks__/pkgcloud.ts @@ -13,21 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { OpenstackProviderOptions } from 'pkgcloud'; import fs from 'fs-extra'; import os from 'os'; import path from 'path'; +import { ObjectWritableMock, BufferReadableMock } from 'stream-mock'; const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; const checkFileExists = async (Key: string): Promise => { // Key will always have / as file separator irrespective of OS since S3 expects /. // Normalize Key to OS specific path before checking if file exists. - const relativeFilePath = Key.split(path.posix.sep).join(path.sep); const filePath = path.join(rootDir, Key); try { - await fs.access(filePath, fs.constants.F_OK); + fs.accessSync(filePath, fs.constants.F_OK); return true; } catch (err) { return false; @@ -38,11 +37,11 @@ class PkgCloudStorageClient { getFile( containerName: string, file: string, - callback: (err: string, file: string) => any, + callback: (err: any, file: string) => any, ) { checkFileExists(file).then(res => { if (!res) { - callback('File does not exist', undefined); + callback('File does not exist', file); throw new Error('File does not exist'); } else { callback(undefined, 'success'); @@ -55,40 +54,28 @@ class PkgCloudStorageClient { callback: (err: string, container: string) => any, ) { if (containerName !== 'mock') { - callback("Container doesn't exist", undefined); + callback("Container doesn't exist", containerName); throw new Error('Container does not exist'); } else { - callback(undefined, 'success'); + callback('Container does not exist', 'success'); } } - upload({ containerName, remote }: { containerName: string; remote: string }) { - checkFileExists(remote).then(res => { - if (!res) { - return new Error("File doesn't exists"); - } - return fs.createWriteStream(`${containerName}/${remote}`); - }); + upload() { + return new ObjectWritableMock(); } - download({ - containerName, - remote, - }: { - containerName: string; - remote: string; - }) { - checkFileExists(remote).then(res => { - if (!res) { - return new Error("File doesn't exists"); - } - return fs.createReadStream(remote); + download() { + const stringify = JSON.stringify({ + "site_description": 'site_content', + "site_name": "backstage" }); + return new BufferReadableMock([stringify]); } } export class storage { - static createClient(params: OpenstackProviderOptions) { + static createClient() { return new PkgCloudStorageClient(); } } diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index a543addfa9..2598de1dce 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -58,6 +58,7 @@ "p-limit": "^3.1.0", "pkgcloud": "^2.2.0", "recursive-readdir": "^2.2.2", + "stream-mock": "^2.0.5", "winston": "^3.2.1" }, "devDependencies": { diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts new file mode 100644 index 0000000000..fab0bf3a12 --- /dev/null +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts @@ -0,0 +1,248 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { + Entity, + EntityName, + ENTITY_DEFAULT_NAMESPACE, +} from '@backstage/catalog-model'; +import { ConfigReader } from '@backstage/config'; +import mockFs from 'mock-fs'; +import os from 'os'; +import path from 'path'; +import * as winston from 'winston'; +import { OpenStackSwiftPublish } from './openStackSwift'; +import { PublisherBase, TechDocsMetadata } from './types'; + +// NOTE: /packages/techdocs-common/__mocks__ is being used to mock pkgcloud client library + +const createMockEntity = (annotations = {}): Entity => { + return { + apiVersion: 'version', + kind: 'TestKind', + metadata: { + name: 'test-component-name', + namespace: 'test-namespace', + annotations: { + ...annotations, + }, + }, + }; +}; + +const createMockEntityName = (): EntityName => ({ + kind: 'TestKind', + name: 'test-component-name', + namespace: 'test-namespace', +}); + +const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; + +const getEntityRootDir = (entity: Entity) => { + const { + kind, + metadata: { namespace, name }, + } = entity; + + return path.join(rootDir, namespace || ENTITY_DEFAULT_NAMESPACE, kind, name); +}; + +const logger = winston.createLogger(); +jest.spyOn(logger, 'info').mockReturnValue(logger); +jest.spyOn(logger, 'error').mockReturnValue(logger); + +let publisher: PublisherBase; + +beforeEach(() => { + mockFs.restore(); + const mockConfig = new ConfigReader({ + techdocs: { + requestUrl: 'http://localhost:7000', + publisher: { + type: 'openStackSwift', + openStackSwift: { + username: 'mockuser', + password: 'verystrongpass', + authUrl: 'mockauthurl', + region: 'mockregion', + containerName: 'mock', + }, + }, + }, + }); + + publisher = OpenStackSwiftPublish.fromConfig(mockConfig, logger); +}); + +describe('OpenStackSwiftPublish', () => { + describe('publish', () => { + beforeEach(() => { + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'index.html': '', + '404.html': '', + assets: { + 'main.css': '', + }, + }, + }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + it('should publish a directory', async () => { + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + setTimeout(async () => { + expect( + await publisher.publish({ + entity, + directory: entityRootDir, + }), + ).toBeUndefined() + }, 5000); + }); + + it('should fail to publish a directory', async () => { + expect.assertions(3); + const wrongPathToGeneratedDirectory = path.join( + rootDir, + 'wrong', + 'path', + 'to', + 'generatedDirectory', + ); + + const entity = createMockEntity(); + await expect( + publisher.publish({ + entity, + directory: wrongPathToGeneratedDirectory, + }), + ).rejects.toThrowError(); + + await publisher + .publish({ + entity, + directory: wrongPathToGeneratedDirectory, + }) + .catch(error => { + expect(error.message).toEqual( + // Can not do exact error message match due to mockFs adding unexpected characters in the path when throwing the error + // Issue reported https://github.com/tschaub/mock-fs/issues/118 + expect.stringContaining( + `Unable to upload file(s) to OpenStack Swift. Error: Failed to read template directory: ENOENT, no such file or directory`, + ), + ); + expect(error.message).toEqual( + expect.stringContaining(wrongPathToGeneratedDirectory), + ); + }); + mockFs.restore(); + }); + }); + + describe('hasDocsBeenGenerated', () => { + it('should return true if docs has been generated', async () => { + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'index.html': 'file-content', + }, + }); + + expect(await publisher.hasDocsBeenGenerated(entity)).toBe(true); + mockFs.restore(); + }); + + it('should return false if docs has not been generated', async () => { + const entity = createMockEntity(); + + expect(await publisher.hasDocsBeenGenerated(entity)).toBe(false); + }); + }); + + describe('fetchTechDocsMetadata', () => { + it('should return tech docs metadata', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': + '{"site_name": "backstage", "site_description": "site_content"}', + }, + }); + + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return tech docs metadata when json encoded with single quotes', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + mockFs({ + [entityRootDir]: { + 'techdocs_metadata.json': `{'site_name': 'backstage', 'site_description': 'site_content'}`, + }, + }); + + const expectedMetadata: TechDocsMetadata = { + site_name: 'backstage', + site_description: 'site_content', + }; + expect( + await publisher.fetchTechDocsMetadata(entityNameMock), + ).toStrictEqual(expectedMetadata); + mockFs.restore(); + }); + + it('should return an error if the techdocs_metadata.json file is not present', async () => { + const entityNameMock = createMockEntityName(); + const entity = createMockEntity(); + const entityRootDir = getEntityRootDir(entity); + + await publisher + .fetchTechDocsMetadata(entityNameMock) + .catch(error => + expect(error).toEqual( + new Error( + `TechDocs metadata fetch failed, The file ${path.join( + entityRootDir, + 'techdocs_metadata.json', + )} does not exist !`, + ), + ), + ); + }); + }); +}); diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts index 15d8b20332..60c8349dee 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -107,7 +107,6 @@ export class OpenStackSwiftPublish implements PublisherBase { * Directory structure used in the bucket is - entityNamespace/entityKind/entityName/index.html */ async publish({ entity, directory }: PublishRequest): Promise { - this.logger.info(`Publish Called hey`); try { // Note: OpenStack Swift manages creation of parent directories if they do not exist. // So collecting path of only the files is good enough. @@ -139,8 +138,7 @@ export class OpenStackSwiftPublish implements PublisherBase { }; // Rate limit the concurrent execution of file uploads to batches of 10 (per publish) - const uploadFile = limiter( - () => + const uploadFile = limiter(() => new Promise((res, rej) => { const writeStream = this.storageClient.upload(params); @@ -168,7 +166,6 @@ export class OpenStackSwiftPublish implements PublisherBase { async fetchTechDocsMetadata( entityName: EntityName, ): Promise { - this.logger.info(`fetchTechDocsMetadata Called hey`); try { return await new Promise(async (resolve, reject) => { const entityRootDir = `${entityName.namespace}/${entityName.kind}/${entityName.name}`; @@ -206,7 +203,6 @@ export class OpenStackSwiftPublish implements PublisherBase { */ docsRouter(): express.Handler { return async (req, res) => { - this.logger.info(`docsRouter Called hey`); // Trim the leading forward slash // filePath example - /default/Component/documented-component/index.html @@ -243,7 +239,6 @@ export class OpenStackSwiftPublish implements PublisherBase { */ async hasDocsBeenGenerated(entity: Entity): Promise { try { - this.logger.info(`hasDocsBeenGenerated Called hey`); const entityRootDir = `${entity.metadata.namespace}/${entity.kind}/${entity.metadata.name}`; return new Promise(res => { @@ -251,7 +246,6 @@ export class OpenStackSwiftPublish implements PublisherBase { this.containerName, `${entityRootDir}/index.html`, (err: any, file: any) => { - console.log(file); if (!err && file) { res(true); } else res(false); diff --git a/yarn.lock b/yarn.lock index f941b3ee7c..5b7893c852 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1816,35 +1816,78 @@ to-fast-properties "^2.0.0" "@backstage/catalog-model@^0.2.0": - version "0.7.2" + version "0.2.0" + resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.2.0.tgz#e3fe2a4ddeb6a9b6ec480c80cb2b9c39cb245576" + integrity sha512-Y1ocdRpBlxK/VrJQjHlQd0bgADECd1B2NRjwd8ss46ibT5hwLvMOfD80+Fa7oPLu0ktJrH4lq0pNIIJIml48zA== dependencies: - "@backstage/config" "^0.1.3" + "@backstage/config" "^0.1.1" "@types/json-schema" "^7.0.5" "@types/yup" "^0.29.8" - ajv "^7.0.3" json-schema "^0.2.5" lodash "^4.17.15" uuid "^8.0.0" yup "^0.29.3" "@backstage/catalog-model@^0.3.0": - version "0.7.2" + version "0.3.1" + resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.3.1.tgz#45d08e2f333c9c566b2bf2629fd707fe989bb404" + integrity sha512-9XhV7c4rmVW+Yzj2PiwTQ7DsegWGB3C4ELsDRExuEVZONdqNcC02cyJtrt3fT5F31ZS3tHkB9bMUymFOBLqUSA== dependencies: - "@backstage/config" "^0.1.3" + "@backstage/config" "^0.1.1" "@types/json-schema" "^7.0.5" "@types/yup" "^0.29.8" - ajv "^7.0.3" json-schema "^0.2.5" lodash "^4.17.15" uuid "^8.0.0" yup "^0.29.3" "@backstage/core@^0.3.0": - version "0.6.3" + version "0.3.2" + resolved "https://registry.npmjs.org/@backstage/core/-/core-0.3.2.tgz#a8209126d5076cf4a8b9bd632fe4e5e2edb62916" + integrity sha512-i5d+Wh8js4qEWoAsPY5L7HVSWpumr1OhfF2dUCGYdyW6AMqVJPca6+n6zp1Rg2CO+J9norp44XAVVCbyhtUpig== dependencies: - "@backstage/config" "^0.1.3" - "@backstage/core-api" "^0.2.11" - "@backstage/theme" "^0.2.3" + "@backstage/config" "^0.1.1" + "@backstage/core-api" "^0.2.1" + "@backstage/theme" "^0.2.1" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@types/dagre" "^0.7.44" + "@types/react" "^16.9" + "@types/react-sparklines" "^1.7.0" + classnames "^2.2.6" + clsx "^1.1.0" + d3-selection "^2.0.0" + d3-shape "^2.0.0" + d3-zoom "^2.0.0" + dagre "^0.8.5" + immer "^7.0.9" + lodash "^4.17.15" + material-table "^1.69.1" + prop-types "^15.7.2" + qs "^6.9.4" + rc-progress "^3.0.0" + react "^16.12.0" + react-dom "^16.12.0" + react-helmet "6.1.0" + react-hook-form "^6.6.0" + react-markdown "^5.0.2" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-sparklines "^1.7.0" + react-syntax-highlighter "^13.5.1" + react-use "^15.3.3" + remark-gfm "^1.0.0" + zen-observable "^0.8.15" + +"@backstage/core@^0.5.0": + version "0.5.0" + resolved "https://registry.npmjs.org/@backstage/core/-/core-0.5.0.tgz#6ff384adc595c18c7db60b9b2d23ebbb9086ed36" + integrity sha512-lCxgKBavUlLYZjZmRF8A7koP4NUhK/tbdf9SaEod0miZg6JTaDoAm3dmHPyqrMBHgoRRCDTxRIxNhj/8vY87oA== + dependencies: + "@backstage/config" "^0.1.2" + "@backstage/core-api" "^0.2.8" + "@backstage/theme" "^0.2.2" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" @@ -1877,20 +1920,53 @@ remark-gfm "^1.0.0" zen-observable "^0.8.15" -"@backstage/plugin-catalog@^0.2.1": - version "0.4.0" +"@backstage/plugin-catalog-react@^0.0.2": + version "0.0.2" + resolved "https://registry.npmjs.org/@backstage/plugin-catalog-react/-/plugin-catalog-react-0.0.2.tgz#e50da2dac9fab3a0d5973f8d1083ee2c368e5e52" + integrity sha512-O6aujFPRaEFTk4XlwOoswbnoHIOqMtj6ycUj6R1mNKOM4plUgGDKKhO3be69FHMJEMbiSvVe6AW+1kXaK+1LqA== + dependencies: + "@backstage/catalog-client" "^0.3.5" + "@backstage/catalog-model" "^0.7.1" + "@backstage/core" "^0.6.0" + "@material-ui/core" "^4.11.0" + "@types/react" "^16.9" + react "^16.13.1" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-use "^15.3.3" + +"@backstage/plugin-catalog-react@^0.0.4": + version "0.0.4" + resolved "https://registry.npmjs.org/@backstage/plugin-catalog-react/-/plugin-catalog-react-0.0.4.tgz#a4c8ba90cf48106ac6af2e03afa6338010a1299b" + integrity sha512-1fAqULJvLyE+3SeZ2yxDJnJ3SbUFv2Im55d3KbMgRaSog1chSJJoO3jbIwIRQIBXjRmCXrZbf56qwwWwxj6OjA== dependencies: "@backstage/catalog-client" "^0.3.6" - "@backstage/catalog-model" "^0.7.2" - "@backstage/core" "^0.6.3" - "@backstage/plugin-catalog-react" "^0.1.0" - "@backstage/theme" "^0.2.3" + "@backstage/catalog-model" "^0.7.1" + "@backstage/core" "^0.6.2" + "@material-ui/core" "^4.11.0" + "@types/react" "^16.9" + react "^16.13.1" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-use "^15.3.3" + +"@backstage/plugin-catalog@^0.2.1": + version "0.2.14" + resolved "https://registry.npmjs.org/@backstage/plugin-catalog/-/plugin-catalog-0.2.14.tgz#50a4176a55ffa543a426ec78cbc9deaecdbcf2b7" + integrity sha512-lDmNcC+m1zbbzYATUp5yIZ5PUp+YyBc1KKu3CCgqjLWSbJ1aJrU1N4g59euel1l2+qSW+lH76Kkp6ZYpZbSO9A== + dependencies: + "@backstage/catalog-client" "^0.3.5" + "@backstage/catalog-model" "^0.7.0" + "@backstage/core" "^0.5.0" + "@backstage/plugin-scaffolder" "^0.4.1" + "@backstage/theme" "^0.2.2" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" "@types/react" "^16.9" classnames "^2.2.6" git-url-parse "^11.4.4" + moment "^2.26.0" react "^16.13.1" react-dom "^16.13.1" react-helmet "6.1.0" @@ -1900,12 +1976,15 @@ swr "^0.3.0" "@backstage/plugin-catalog@^0.3.1": - version "0.4.0" + version "0.3.2" + resolved "https://registry.npmjs.org/@backstage/plugin-catalog/-/plugin-catalog-0.3.2.tgz#06945f10fd678efdade3f2795590c12433568fa0" + integrity sha512-iHLxPHRN9nYIXwOEAQ06m+PagsFb6Nb/XjJSebCAnSrAxPPITvBCfxc2H1GbyyMdC7KAr1ozORB/FFkNsCaQJg== dependencies: "@backstage/catalog-client" "^0.3.6" - "@backstage/catalog-model" "^0.7.2" - "@backstage/core" "^0.6.3" - "@backstage/plugin-catalog-react" "^0.1.0" + "@backstage/catalog-model" "^0.7.1" + "@backstage/core" "^0.6.2" + "@backstage/plugin-catalog-react" "^0.0.4" + "@backstage/plugin-scaffolder" "^0.5.1" "@backstage/theme" "^0.2.3" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" @@ -1946,6 +2025,56 @@ react-use "^15.3.3" swr "^0.3.0" +"@backstage/plugin-scaffolder@^0.4.1": + version "0.4.2" + resolved "https://registry.npmjs.org/@backstage/plugin-scaffolder/-/plugin-scaffolder-0.4.2.tgz#58159227997f7e248ce52535bc32f19fcd0990dc" + integrity sha512-YuyHM587Rqg6KufxfFqQdI7dsZniBM/11Aj8Q0m5ZszOpCuNmDDkR1VX8MKHTBJ709mnLAqRgArdla7FOrOAXQ== + dependencies: + "@backstage/catalog-model" "^0.7.1" + "@backstage/core" "^0.6.0" + "@backstage/plugin-catalog-react" "^0.0.2" + "@backstage/theme" "^0.2.3" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@rjsf/core" "^2.4.0" + "@rjsf/material-ui" "^2.4.0" + classnames "^2.2.6" + git-url-parse "^11.4.4" + moment "^2.26.0" + react "^16.13.1" + react-dom "^16.13.1" + react-lazylog "^4.5.2" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-use "^15.3.3" + swr "^0.3.0" + +"@backstage/plugin-scaffolder@^0.5.1": + version "0.5.1" + resolved "https://registry.npmjs.org/@backstage/plugin-scaffolder/-/plugin-scaffolder-0.5.1.tgz#9d36f6b01991ddd9f9f2996068f3f31c766db8db" + integrity sha512-EG+iUc107bneVBPQpFKGp2jD9Y9+x50g/gY6TBN1je8TkgyluoxMj7wKv9e+d4TeGzXwK/LW/suajG9Zo0TJGQ== + dependencies: + "@backstage/catalog-model" "^0.7.1" + "@backstage/core" "^0.6.2" + "@backstage/plugin-catalog-react" "^0.0.4" + "@backstage/theme" "^0.2.3" + "@material-ui/core" "^4.11.0" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@rjsf/core" "^2.4.0" + "@rjsf/material-ui" "^2.4.0" + classnames "^2.2.6" + git-url-parse "^11.4.4" + moment "^2.26.0" + react "^16.13.1" + react-dom "^16.13.1" + react-lazylog "^4.5.2" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-use "^15.3.3" + swr "^0.3.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -15076,6 +15205,11 @@ immer@1.10.0: resolved "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== +immer@^7.0.9: + version "7.0.15" + resolved "https://registry.npmjs.org/immer/-/immer-7.0.15.tgz#dc3bc6db87401659d2e737c67a21b227c484a4ad" + integrity sha512-yM7jo9+hvYgvdCQdqvhCNRRio0SCXc8xDPzA25SvKWa7b1WVPjLwQs1VYU5JPXjcJPTqAa5NP5dqpORGYBQ2AA== + immer@^8.0.1: version "8.0.1" resolved "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" @@ -18630,7 +18764,7 @@ modify-values@^1.0.0: resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment@^2.19.3, moment@^2.25.3, moment@^2.27.0: +moment@^2.19.3, moment@^2.25.3, moment@^2.26.0, moment@^2.27.0: version "2.29.1" resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== @@ -24025,6 +24159,11 @@ stream-http@^2.7.2: to-arraybuffer "^1.0.0" xtend "^4.0.0" +stream-mock@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/stream-mock/-/stream-mock-2.0.5.tgz#c99d24bd6dbb0eaa57cf6ffefdb064150747826e" + integrity sha512-dx9skT8QYjwLsal+MhGHr4UtgS49brw851C/oTixmhCi4Ip+/qnZmhV1qOcznYYAED6gYKmKea+jjza4/wjpSg== + stream-shift@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" From cebda81000ec35d680d1c6d0a8f98eb6e4ff9a8c Mon Sep 17 00:00:00 2001 From: erdoganoksuz Date: Thu, 25 Feb 2021 17:24:49 +0300 Subject: [PATCH 08/17] documentation added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- docs/features/techdocs/using-cloud-storage.md | 91 +++++++++ .../techdocs-common/__mocks__/pkgcloud.ts | 44 ++++- packages/techdocs-common/package.json | 3 +- .../src/stages/publish/openStackSwift.test.ts | 14 +- .../src/stages/publish/openStackSwift.ts | 24 +-- plugins/techdocs-backend/package.json | 2 +- yarn.lock | 184 +++--------------- 7 files changed, 171 insertions(+), 191 deletions(-) diff --git a/docs/features/techdocs/using-cloud-storage.md b/docs/features/techdocs/using-cloud-storage.md index cd4b058644..66e6e15811 100644 --- a/docs/features/techdocs/using-cloud-storage.md +++ b/docs/features/techdocs/using-cloud-storage.md @@ -309,3 +309,94 @@ and read the static generated documentation files. When you start the backend of the app, you should be able to see `techdocs info Successfully connected to the Azure Blob Storage container` in the logs. + +## Configuring OpenStack Swift Container with TechDocs + +Follow the +[official OpenStack Api documentation](https://docs.openstack.org/api-ref/identity/v3/) +for the latest instructions on the following steps involving Azure Blob Storage. + +**1. Set `techdocs.publisher.type` config in your `app-config.yaml`** + +Set `techdocs.publisher.type` to `'openStackSwift'`. + +```yaml +techdocs: + publisher: + type: 'openStackSwift' +``` + +**2. Create an Azure Blob Storage Container** + +Create a dedicated container for TechDocs sites. +[Refer to the official documentation](https://docs.openstack.org/mitaka/user-guide/dashboard_manage_containers.html). + +TechDocs will publish documentation to this container and will fetch files from +here to serve documentation in Backstage. Note that the container names are +globally unique. + +Set the config `techdocs.publisher.openStackSwift.containerName` in your +`app-config.yaml` to the name of the container you just created. + +```yaml +techdocs: + publisher: + type: 'openStackSwift' + openStackSwift: + containerName: 'name-of-techdocs-storage-container' +``` + +**3a. (Recommended) Authentication using environment variable** + +Set the config `techdocs.publisher.openStackSwift.accountName` in +your `app-config.yaml` to the your account name. + +The storage blob client will automatically use the environment variable +`AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET` to authenticate with +Azure Blob Storage. +[Steps to create the service where the variables can be retrieved from](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal). + +https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad for more +details. + +```yaml +techdocs: + publisher: + type: 'azureBlobStorage' + azureBlobStorage: + containerName: 'name-of-techdocs-storage-bucket' + credentials: + accountName: + $env: TECHDOCS_AZURE_BLOB_STORAGE_ACCOUNT_NAME +``` + +**3b. Authentication using app-config.yaml** + +If you do not prefer (3a) and optionally like to use a service account, you can +follow these steps. + +To get credentials, access the Azure Portal and go to "Settings > Access Keys", +and get your Storage account name and Primary Key. +https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key +for more details. + +```yaml +techdocs: + publisher: + type: 'azureBlobStorage' + azureBlobStorage: + containerName: 'name-of-techdocs-storage-bucket' + credentials: + accountName: + $env: TECHDOCS_AZURE_BLOB_STORAGE_ACCOUNT_NAME + accountKey: + $env: TECHDOCS_AZURE_BLOB_STORAGE_ACCOUNT_KEY +``` + +**4. That's it!** + +Your Backstage app is now ready to use Azure Blob Storage for TechDocs, to store +and read the static generated documentation files. When you start the backend of +the app, you should be able to see +`techdocs info Successfully connected to the Azure Blob Storage container` in +the logs. diff --git a/packages/techdocs-common/__mocks__/pkgcloud.ts b/packages/techdocs-common/__mocks__/pkgcloud.ts index bf729a7bd2..4316630928 100644 --- a/packages/techdocs-common/__mocks__/pkgcloud.ts +++ b/packages/techdocs-common/__mocks__/pkgcloud.ts @@ -16,7 +16,7 @@ import fs from 'fs-extra'; import os from 'os'; import path from 'path'; -import { ObjectWritableMock, BufferReadableMock } from 'stream-mock'; +import { EventEmitter } from 'events'; const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; @@ -61,16 +61,44 @@ class PkgCloudStorageClient { } } - upload() { - return new ObjectWritableMock(); + upload({ remote }: { remote: string }) { + const filePath = path.join(rootDir, remote); + + const emitter = new EventEmitter(); + + process.nextTick(() => { + if (fs.existsSync(filePath)) { + emitter.emit('success'); + (emitter as any).end = () => true; + } else { + emitter.emit( + 'error', + new Error(`The file ${filePath} does not exist !`), + ); + } + }); + + return emitter; } - download() { - const stringify = JSON.stringify({ - "site_description": 'site_content', - "site_name": "backstage" + download({ remote }: { remote: string }) { + const filePath = path.join(rootDir, remote); + + const emitter = new EventEmitter(); + + process.nextTick(() => { + if (fs.existsSync(filePath)) { + emitter.emit('data', Buffer.from(fs.readFileSync(filePath))); + emitter.emit('end'); + } else { + emitter.emit( + 'error', + new Error(`The file ${filePath} does not exist !`), + ); + } }); - return new BufferReadableMock([stringify]); + + return emitter; } } diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index 2598de1dce..62d7b73f73 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/techdocs-common", "description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "0.4.2", + "version": "0.5.0", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -58,7 +58,6 @@ "p-limit": "^3.1.0", "pkgcloud": "^2.2.0", "recursive-readdir": "^2.2.2", - "stream-mock": "^2.0.5", "winston": "^3.2.1" }, "devDependencies": { diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts index fab0bf3a12..30ec8a59b9 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts @@ -111,14 +111,12 @@ describe('OpenStackSwiftPublish', () => { const entity = createMockEntity(); const entityRootDir = getEntityRootDir(entity); - setTimeout(async () => { - expect( - await publisher.publish({ - entity, - directory: entityRootDir, - }), - ).toBeUndefined() - }, 5000); + expect( + await publisher.publish({ + entity, + directory: entityRootDir, + }), + ).toBeUndefined() }); it('should fail to publish a directory', async () => { diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts index 60c8349dee..4c4c15d9c4 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -49,7 +49,7 @@ export class OpenStackSwiftPublish implements PublisherBase { } catch (error) { throw new Error( "Since techdocs.publisher.type is set to 'openStackSwift' in your app config, " + - 'techdocs.publisher.openStackSwift.containerName is required.', + 'techdocs.publisher.openStackSwift.containerName is required.', ); } @@ -59,8 +59,8 @@ export class OpenStackSwiftPublish implements PublisherBase { const storageClient = storage.createClient({ provider: 'openstack', - username: openStackSwiftConfig.getString('username'), - password: openStackSwiftConfig.getString('password'), + username: openStackSwiftConfig.getString('credentials.username'), + password: openStackSwiftConfig.getString('credentials.password'), authUrl: openStackSwiftConfig.getString('authUrl'), keystoneAuthVersion: openStackSwiftConfig.getOptionalString('keystoneAuthVersion') || 'v3', @@ -80,9 +80,9 @@ export class OpenStackSwiftPublish implements PublisherBase { } else { logger.error( `Could not retrieve metadata about the OpenStack Swift container ${containerName}. ` + - 'Make sure the container exists. Also make sure that authentication is setup either by ' + - 'explicitly defining credentials and region in techdocs.publisher.openStackSwift in app config or ' + - 'by using environment variables. Refer to https://backstage.io/docs/features/techdocs/using-cloud-storage', + 'Make sure the container exists. Also make sure that authentication is setup either by ' + + 'explicitly defining credentials and region in techdocs.publisher.openStackSwift in app config or ' + + 'by using environment variables. Refer to https://backstage.io/docs/features/techdocs/using-cloud-storage', ); logger.error(`from OpenStack client library: ${err.message}`); @@ -139,15 +139,15 @@ export class OpenStackSwiftPublish implements PublisherBase { // Rate limit the concurrent execution of file uploads to batches of 10 (per publish) const uploadFile = limiter(() => - new Promise((res, rej) => { - const writeStream = this.storageClient.upload(params); + new Promise((res, rej) => { + const writeStream = this.storageClient.upload(params); - writeStream.on('error', rej); + writeStream.on('error', rej); - writeStream.on('success', res); + writeStream.on('success', res); - readStream.pipe(writeStream); - }), + readStream.pipe(writeStream); + }), ); uploadPromises.push(uploadFile); } diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 03bea5233d..0de3393097 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.6.2", + "version": "0.7.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/yarn.lock b/yarn.lock index 5b7893c852..0f2cbbb7d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1816,85 +1816,44 @@ to-fast-properties "^2.0.0" "@backstage/catalog-model@^0.2.0": - version "0.2.0" - resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.2.0.tgz#e3fe2a4ddeb6a9b6ec480c80cb2b9c39cb245576" - integrity sha512-Y1ocdRpBlxK/VrJQjHlQd0bgADECd1B2NRjwd8ss46ibT5hwLvMOfD80+Fa7oPLu0ktJrH4lq0pNIIJIml48zA== + version "0.7.2" dependencies: - "@backstage/config" "^0.1.1" + "@backstage/config" "^0.1.3" "@types/json-schema" "^7.0.5" "@types/yup" "^0.29.8" + ajv "^7.0.3" json-schema "^0.2.5" lodash "^4.17.15" uuid "^8.0.0" yup "^0.29.3" "@backstage/catalog-model@^0.3.0": - version "0.3.1" - resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.3.1.tgz#45d08e2f333c9c566b2bf2629fd707fe989bb404" - integrity sha512-9XhV7c4rmVW+Yzj2PiwTQ7DsegWGB3C4ELsDRExuEVZONdqNcC02cyJtrt3fT5F31ZS3tHkB9bMUymFOBLqUSA== + version "0.7.2" dependencies: - "@backstage/config" "^0.1.1" + "@backstage/config" "^0.1.3" "@types/json-schema" "^7.0.5" "@types/yup" "^0.29.8" + ajv "^7.0.3" json-schema "^0.2.5" lodash "^4.17.15" uuid "^8.0.0" yup "^0.29.3" "@backstage/core@^0.3.0": - version "0.3.2" - resolved "https://registry.npmjs.org/@backstage/core/-/core-0.3.2.tgz#a8209126d5076cf4a8b9bd632fe4e5e2edb62916" - integrity sha512-i5d+Wh8js4qEWoAsPY5L7HVSWpumr1OhfF2dUCGYdyW6AMqVJPca6+n6zp1Rg2CO+J9norp44XAVVCbyhtUpig== + version "0.6.3" dependencies: - "@backstage/config" "^0.1.1" - "@backstage/core-api" "^0.2.1" - "@backstage/theme" "^0.2.1" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@types/dagre" "^0.7.44" - "@types/react" "^16.9" - "@types/react-sparklines" "^1.7.0" - classnames "^2.2.6" - clsx "^1.1.0" - d3-selection "^2.0.0" - d3-shape "^2.0.0" - d3-zoom "^2.0.0" - dagre "^0.8.5" - immer "^7.0.9" - lodash "^4.17.15" - material-table "^1.69.1" - prop-types "^15.7.2" - qs "^6.9.4" - rc-progress "^3.0.0" - react "^16.12.0" - react-dom "^16.12.0" - react-helmet "6.1.0" - react-hook-form "^6.6.0" - react-markdown "^5.0.2" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-sparklines "^1.7.0" - react-syntax-highlighter "^13.5.1" - react-use "^15.3.3" - remark-gfm "^1.0.0" - zen-observable "^0.8.15" - -"@backstage/core@^0.5.0": - version "0.5.0" - resolved "https://registry.npmjs.org/@backstage/core/-/core-0.5.0.tgz#6ff384adc595c18c7db60b9b2d23ebbb9086ed36" - integrity sha512-lCxgKBavUlLYZjZmRF8A7koP4NUhK/tbdf9SaEod0miZg6JTaDoAm3dmHPyqrMBHgoRRCDTxRIxNhj/8vY87oA== - dependencies: - "@backstage/config" "^0.1.2" - "@backstage/core-api" "^0.2.8" - "@backstage/theme" "^0.2.2" + "@backstage/config" "^0.1.3" + "@backstage/core-api" "^0.2.11" + "@backstage/theme" "^0.2.3" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" + "@testing-library/react-hooks" "^3.4.2" "@types/dagre" "^0.7.44" "@types/prop-types" "^15.7.3" "@types/react" "^16.9" "@types/react-sparklines" "^1.7.0" + "@types/react-text-truncate" "^0.14.0" classnames "^2.2.6" clsx "^1.1.0" d3-selection "^2.0.0" @@ -1916,57 +1875,25 @@ react-router-dom "6.0.0-beta.0" react-sparklines "^1.7.0" react-syntax-highlighter "^13.5.1" + react-text-truncate "^0.16.0" react-use "^15.3.3" remark-gfm "^1.0.0" zen-observable "^0.8.15" -"@backstage/plugin-catalog-react@^0.0.2": - version "0.0.2" - resolved "https://registry.npmjs.org/@backstage/plugin-catalog-react/-/plugin-catalog-react-0.0.2.tgz#e50da2dac9fab3a0d5973f8d1083ee2c368e5e52" - integrity sha512-O6aujFPRaEFTk4XlwOoswbnoHIOqMtj6ycUj6R1mNKOM4plUgGDKKhO3be69FHMJEMbiSvVe6AW+1kXaK+1LqA== - dependencies: - "@backstage/catalog-client" "^0.3.5" - "@backstage/catalog-model" "^0.7.1" - "@backstage/core" "^0.6.0" - "@material-ui/core" "^4.11.0" - "@types/react" "^16.9" - react "^16.13.1" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-use "^15.3.3" - -"@backstage/plugin-catalog-react@^0.0.4": - version "0.0.4" - resolved "https://registry.npmjs.org/@backstage/plugin-catalog-react/-/plugin-catalog-react-0.0.4.tgz#a4c8ba90cf48106ac6af2e03afa6338010a1299b" - integrity sha512-1fAqULJvLyE+3SeZ2yxDJnJ3SbUFv2Im55d3KbMgRaSog1chSJJoO3jbIwIRQIBXjRmCXrZbf56qwwWwxj6OjA== +"@backstage/plugin-catalog@^0.2.1": + version "0.4.0" dependencies: "@backstage/catalog-client" "^0.3.6" - "@backstage/catalog-model" "^0.7.1" - "@backstage/core" "^0.6.2" - "@material-ui/core" "^4.11.0" - "@types/react" "^16.9" - react "^16.13.1" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-use "^15.3.3" - -"@backstage/plugin-catalog@^0.2.1": - version "0.2.14" - resolved "https://registry.npmjs.org/@backstage/plugin-catalog/-/plugin-catalog-0.2.14.tgz#50a4176a55ffa543a426ec78cbc9deaecdbcf2b7" - integrity sha512-lDmNcC+m1zbbzYATUp5yIZ5PUp+YyBc1KKu3CCgqjLWSbJ1aJrU1N4g59euel1l2+qSW+lH76Kkp6ZYpZbSO9A== - dependencies: - "@backstage/catalog-client" "^0.3.5" - "@backstage/catalog-model" "^0.7.0" - "@backstage/core" "^0.5.0" - "@backstage/plugin-scaffolder" "^0.4.1" - "@backstage/theme" "^0.2.2" + "@backstage/catalog-model" "^0.7.2" + "@backstage/core" "^0.6.3" + "@backstage/plugin-catalog-react" "^0.1.0" + "@backstage/theme" "^0.2.3" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" "@types/react" "^16.9" classnames "^2.2.6" git-url-parse "^11.4.4" - moment "^2.26.0" react "^16.13.1" react-dom "^16.13.1" react-helmet "6.1.0" @@ -1976,15 +1903,12 @@ swr "^0.3.0" "@backstage/plugin-catalog@^0.3.1": - version "0.3.2" - resolved "https://registry.npmjs.org/@backstage/plugin-catalog/-/plugin-catalog-0.3.2.tgz#06945f10fd678efdade3f2795590c12433568fa0" - integrity sha512-iHLxPHRN9nYIXwOEAQ06m+PagsFb6Nb/XjJSebCAnSrAxPPITvBCfxc2H1GbyyMdC7KAr1ozORB/FFkNsCaQJg== + version "0.4.0" dependencies: "@backstage/catalog-client" "^0.3.6" - "@backstage/catalog-model" "^0.7.1" - "@backstage/core" "^0.6.2" - "@backstage/plugin-catalog-react" "^0.0.4" - "@backstage/plugin-scaffolder" "^0.5.1" + "@backstage/catalog-model" "^0.7.2" + "@backstage/core" "^0.6.3" + "@backstage/plugin-catalog-react" "^0.1.0" "@backstage/theme" "^0.2.3" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" @@ -2025,56 +1949,6 @@ react-use "^15.3.3" swr "^0.3.0" -"@backstage/plugin-scaffolder@^0.4.1": - version "0.4.2" - resolved "https://registry.npmjs.org/@backstage/plugin-scaffolder/-/plugin-scaffolder-0.4.2.tgz#58159227997f7e248ce52535bc32f19fcd0990dc" - integrity sha512-YuyHM587Rqg6KufxfFqQdI7dsZniBM/11Aj8Q0m5ZszOpCuNmDDkR1VX8MKHTBJ709mnLAqRgArdla7FOrOAXQ== - dependencies: - "@backstage/catalog-model" "^0.7.1" - "@backstage/core" "^0.6.0" - "@backstage/plugin-catalog-react" "^0.0.2" - "@backstage/theme" "^0.2.3" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@rjsf/core" "^2.4.0" - "@rjsf/material-ui" "^2.4.0" - classnames "^2.2.6" - git-url-parse "^11.4.4" - moment "^2.26.0" - react "^16.13.1" - react-dom "^16.13.1" - react-lazylog "^4.5.2" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-use "^15.3.3" - swr "^0.3.0" - -"@backstage/plugin-scaffolder@^0.5.1": - version "0.5.1" - resolved "https://registry.npmjs.org/@backstage/plugin-scaffolder/-/plugin-scaffolder-0.5.1.tgz#9d36f6b01991ddd9f9f2996068f3f31c766db8db" - integrity sha512-EG+iUc107bneVBPQpFKGp2jD9Y9+x50g/gY6TBN1je8TkgyluoxMj7wKv9e+d4TeGzXwK/LW/suajG9Zo0TJGQ== - dependencies: - "@backstage/catalog-model" "^0.7.1" - "@backstage/core" "^0.6.2" - "@backstage/plugin-catalog-react" "^0.0.4" - "@backstage/theme" "^0.2.3" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@rjsf/core" "^2.4.0" - "@rjsf/material-ui" "^2.4.0" - classnames "^2.2.6" - git-url-parse "^11.4.4" - moment "^2.26.0" - react "^16.13.1" - react-dom "^16.13.1" - react-lazylog "^4.5.2" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-use "^15.3.3" - swr "^0.3.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -15205,11 +15079,6 @@ immer@1.10.0: resolved "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== -immer@^7.0.9: - version "7.0.15" - resolved "https://registry.npmjs.org/immer/-/immer-7.0.15.tgz#dc3bc6db87401659d2e737c67a21b227c484a4ad" - integrity sha512-yM7jo9+hvYgvdCQdqvhCNRRio0SCXc8xDPzA25SvKWa7b1WVPjLwQs1VYU5JPXjcJPTqAa5NP5dqpORGYBQ2AA== - immer@^8.0.1: version "8.0.1" resolved "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" @@ -18764,7 +18633,7 @@ modify-values@^1.0.0: resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment@^2.19.3, moment@^2.25.3, moment@^2.26.0, moment@^2.27.0: +moment@^2.19.3, moment@^2.25.3, moment@^2.27.0: version "2.29.1" resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== @@ -24159,11 +24028,6 @@ stream-http@^2.7.2: to-arraybuffer "^1.0.0" xtend "^4.0.0" -stream-mock@^2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/stream-mock/-/stream-mock-2.0.5.tgz#c99d24bd6dbb0eaa57cf6ffefdb064150747826e" - integrity sha512-dx9skT8QYjwLsal+MhGHr4UtgS49brw851C/oTixmhCi4Ip+/qnZmhV1qOcznYYAED6gYKmKea+jjza4/wjpSg== - stream-shift@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" From fc470a310ea2d439ea95d8cdbe2c9f5e92a29139 Mon Sep 17 00:00:00 2001 From: gmzsenturk Date: Thu, 25 Feb 2021 17:50:20 +0300 Subject: [PATCH 09/17] Updated cloud storage documentation for open stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- docs/features/techdocs/using-cloud-storage.md | 69 +++++++------------ 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/docs/features/techdocs/using-cloud-storage.md b/docs/features/techdocs/using-cloud-storage.md index 66e6e15811..e160c6f5f5 100644 --- a/docs/features/techdocs/using-cloud-storage.md +++ b/docs/features/techdocs/using-cloud-storage.md @@ -281,11 +281,9 @@ techdocs: **3b. Authentication using app-config.yaml** -If you do not prefer (3a) and optionally like to use a service account, you can -follow these steps. +Set the config `techdocs.publisher.azureBlobStorage.credentials.accountName` in +your `app-config.yaml` to the your account name. -To get credentials, access the Azure Portal and go to "Settings > Access Keys", -and get your Storage account name and Primary Key. https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key for more details. @@ -314,7 +312,7 @@ the logs. Follow the [official OpenStack Api documentation](https://docs.openstack.org/api-ref/identity/v3/) -for the latest instructions on the following steps involving Azure Blob Storage. +for the latest instructions on the following steps involving Open Stack Storage. **1. Set `techdocs.publisher.type` config in your `app-config.yaml`** @@ -326,7 +324,7 @@ techdocs: type: 'openStackSwift' ``` -**2. Create an Azure Blob Storage Container** +**2. Create an OpenStack Swift Storage Container** Create a dedicated container for TechDocs sites. [Refer to the official documentation](https://docs.openstack.org/mitaka/user-guide/dashboard_manage_containers.html). @@ -346,57 +344,40 @@ techdocs: containerName: 'name-of-techdocs-storage-container' ``` -**3a. (Recommended) Authentication using environment variable** +**3. Authentication using app-config.yaml** -Set the config `techdocs.publisher.openStackSwift.accountName` in -your `app-config.yaml` to the your account name. +Set the configs in your `app-config.yaml` to the your container name. -The storage blob client will automatically use the environment variable -`AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET` to authenticate with -Azure Blob Storage. -[Steps to create the service where the variables can be retrieved from](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal). - -https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad for more -details. - -```yaml -techdocs: - publisher: - type: 'azureBlobStorage' - azureBlobStorage: - containerName: 'name-of-techdocs-storage-bucket' - credentials: - accountName: - $env: TECHDOCS_AZURE_BLOB_STORAGE_ACCOUNT_NAME -``` - -**3b. Authentication using app-config.yaml** - -If you do not prefer (3a) and optionally like to use a service account, you can -follow these steps. - -To get credentials, access the Azure Portal and go to "Settings > Access Keys", -and get your Storage account name and Primary Key. -https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key +https://docs.openstack.org/api-ref/identity/v3/?expanded=password-authentication-with-unscoped-authorization-detail#password-authentication-with-unscoped-authorization for more details. ```yaml techdocs: publisher: - type: 'azureBlobStorage' - azureBlobStorage: + type: 'openStackSwift' + openStackSwift: containerName: 'name-of-techdocs-storage-bucket' credentials: - accountName: - $env: TECHDOCS_AZURE_BLOB_STORAGE_ACCOUNT_NAME - accountKey: - $env: TECHDOCS_AZURE_BLOB_STORAGE_ACCOUNT_KEY + userName: + $env: OPENSTACK_SWIFT_STORAGE_USERNAME + password: + $env: OPENSTACK_SWIFT_STORAGE_PASSWORD + authUrl: + $env: OPENSTACK_SWIFT_STORAGE_AUTH_URL + keystoneAuthVersion: + $env: OPENSTACK_SWIFT_STORAGE_AUTH_VERSION + domainId: + $env: OPENSTACK_SWIFT_STORAGE_DOMAIN_ID + domainName: + $env: OPENSTACK_SWIFT_STORAGE_DOMAIN_NAME + region: + $env: OPENSTACK_SWIFT_STORAGE_REGION ``` **4. That's it!** -Your Backstage app is now ready to use Azure Blob Storage for TechDocs, to store +Your Backstage app is now ready to use OpenStack Swift Storage for TechDocs, to store and read the static generated documentation files. When you start the backend of the app, you should be able to see -`techdocs info Successfully connected to the Azure Blob Storage container` in +`techdocs info Successfully connected to the OpenStack Swift Storage container` in the logs. From 54e711826fb441ee1a7b861f1fec4371e2640562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Can=20Bilgi=C3=A7?= Date: Thu, 25 Feb 2021 17:54:55 +0300 Subject: [PATCH 10/17] capp config type has changed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- app-config.yaml | 2 +- .../src/stages/publish/openStackSwift.test.ts | 8 ++-- .../src/stages/publish/publish.test.ts | 6 ++- plugins/techdocs/config.d.ts | 41 ++++++++++--------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index f68f5a30a2..be0f405de5 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -94,7 +94,7 @@ techdocs: generators: techdocs: 'docker' # Alternatives - 'local' publisher: - type: 'local' # Alternatives - 'googleGcs' or 'awsS3' or 'azureBlobStorage'. Read documentation for using alternatives. + type: 'local' # Alternatives - 'googleGcs' or 'awsS3' or 'azureBlobStorage' or 'openStackSwift' Read documentation for using alternatives. sentry: organization: my-company diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts index 30ec8a59b9..b831012acf 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts @@ -73,8 +73,10 @@ beforeEach(() => { publisher: { type: 'openStackSwift', openStackSwift: { - username: 'mockuser', - password: 'verystrongpass', + credentials: { + username: 'mockuser', + password: 'verystrongpass', + }, authUrl: 'mockauthurl', region: 'mockregion', containerName: 'mock', @@ -116,7 +118,7 @@ describe('OpenStackSwiftPublish', () => { entity, directory: entityRootDir, }), - ).toBeUndefined() + ).toBeUndefined(); }); it('should fail to publish a directory', async () => { diff --git a/packages/techdocs-common/src/stages/publish/publish.test.ts b/packages/techdocs-common/src/stages/publish/publish.test.ts index f9bd0f3934..948b92aaa6 100644 --- a/packages/techdocs-common/src/stages/publish/publish.test.ts +++ b/packages/techdocs-common/src/stages/publish/publish.test.ts @@ -170,8 +170,10 @@ describe('Publisher', () => { publisher: { type: 'openStackSwift', openStackSwift: { - username: 'mockuser', - password: 'verystrongpass', + credentials: { + username: 'mockuser', + password: 'verystrongpass', + }, authUrl: 'mockauthurl', region: 'mockregion', containerName: 'mock', diff --git a/plugins/techdocs/config.d.ts b/plugins/techdocs/config.d.ts index c4fe4a47db..5e12f7dbc7 100644 --- a/plugins/techdocs/config.d.ts +++ b/plugins/techdocs/config.d.ts @@ -89,56 +89,57 @@ export interface Config { type: 'openStackSwift'; /** - * Required when 'type' is set to awsS3 + * Required when 'type' is set to openStackSwift */ openStackSwift?: { /** - * (Optional) Credentials used to access a storage bucket. - * If not set, environment variables or aws config file will be used to authenticate. - * @see https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/loading-node-credentials-environment.html - * @see https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/loading-node-credentials-shared.html + * (Required) Credentials used to access a storage bucket. + * @see https://docs.openstack.org/api-ref/identity/v3/?expanded=password-authentication-with-unscoped-authorization-detail#password-authentication-with-unscoped-authorization * @visibility secret */ + credentials: { + /** + * (Required) Root user name + * @visibility backend + */ + username: string; + /** + * (Required) Root user password + * @visibility backend + */ + password: string; // required + }; /** * (Required) Cloud Storage Container Name * @visibility backend */ containerName: string; - /** - * (Required) Root user name - * @visibility backend - */ - username: string; - /** - * (Required) Root user password - * @visibility backend - */ - password: string; // required /** * (Required) Auth url sometimes OpenStack uses different port check your OpenStack apis. * @visibility backend */ authUrl: string; /** - * (Required) Auth version + * (Optional) Auth version + * If not set, 'v2.0' will be used. * @visibility backend */ keystoneAuthVersion: string; /** - * (Required) Domaind Id + * (Required) Domain Id * @visibility backend */ domainId: string; /** - * (Required) Domaind Name + * (Required) Domain Name * @visibility backend */ - domainName: 'Default'; + domainName: string; /** * (Required) Region * @visibility backend */ - region: 'earth'; + region: string; }; } | { From e0bccb16a22f327224c910470acceefe6b9746fd Mon Sep 17 00:00:00 2001 From: erdoganoksuz Date: Fri, 26 Feb 2021 10:39:08 +0300 Subject: [PATCH 11/17] versions for changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- packages/techdocs-common/package.json | 2 +- plugins/techdocs-backend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index 62d7b73f73..a543addfa9 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/techdocs-common", "description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "0.5.0", + "version": "0.4.2", "main": "src/index.ts", "types": "src/index.ts", "private": false, diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 0de3393097..03bea5233d 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.7.0", + "version": "0.6.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", From 15fb8e2176ac26bd9b371857299c2a5d75b9799c Mon Sep 17 00:00:00 2001 From: erdoganoksuz Date: Fri, 26 Feb 2021 10:41:43 +0300 Subject: [PATCH 12/17] changeset added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- .changeset/odd-mirrors-smell.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/odd-mirrors-smell.md diff --git a/.changeset/odd-mirrors-smell.md b/.changeset/odd-mirrors-smell.md new file mode 100644 index 0000000000..7b6e0285ef --- /dev/null +++ b/.changeset/odd-mirrors-smell.md @@ -0,0 +1,7 @@ +--- +'@backstage/techdocs-common': minor +'@backstage/plugin-techdocs': minor +'@backstage/plugin-techdocs-backend': minor +--- + +OpenStack Swift Publisher added to TechDocs From 2ebfe29d8a04faa06cb95f312bbce1d9daa6825f Mon Sep 17 00:00:00 2001 From: gmzsenturk Date: Fri, 26 Feb 2021 10:55:16 +0300 Subject: [PATCH 13/17] Cloud storage documentation edited MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- docs/features/techdocs/using-cloud-storage.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/features/techdocs/using-cloud-storage.md b/docs/features/techdocs/using-cloud-storage.md index e160c6f5f5..6fefca70c0 100644 --- a/docs/features/techdocs/using-cloud-storage.md +++ b/docs/features/techdocs/using-cloud-storage.md @@ -281,8 +281,11 @@ techdocs: **3b. Authentication using app-config.yaml** -Set the config `techdocs.publisher.azureBlobStorage.credentials.accountName` in -your `app-config.yaml` to the your account name. +If you do not prefer (3a) and optionally like to use a service account, you can Set the config `techdocs.publisher.azureBlobStorage.credentials.accountName` in +follow these steps. your `app-config.yaml` to the your account name. + +To get credentials, access the Azure Portal and go to "Settings > Access Keys", +and get your Storage account name and Primary Key. https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key for more details. From a10174b56d82a0552a809bccab4512ec293b01cb Mon Sep 17 00:00:00 2001 From: gmzsenturk Date: Fri, 26 Feb 2021 11:18:41 +0300 Subject: [PATCH 14/17] Documentations edited MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- app-config.yaml | 2 +- docs/features/techdocs/using-cloud-storage.md | 17 +++++++------- .../techdocs-common/__mocks__/pkgcloud.ts | 2 +- .../src/stages/publish/openStackSwift.ts | 23 ++++++++++--------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index be0f405de5..f963296bb8 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -94,7 +94,7 @@ techdocs: generators: techdocs: 'docker' # Alternatives - 'local' publisher: - type: 'local' # Alternatives - 'googleGcs' or 'awsS3' or 'azureBlobStorage' or 'openStackSwift' Read documentation for using alternatives. + type: 'local' # Alternatives - 'googleGcs' or 'awsS3' or 'azureBlobStorage' or 'openStackSwift'. Read documentation for using alternatives. sentry: organization: my-company diff --git a/docs/features/techdocs/using-cloud-storage.md b/docs/features/techdocs/using-cloud-storage.md index 6fefca70c0..8b8ff04573 100644 --- a/docs/features/techdocs/using-cloud-storage.md +++ b/docs/features/techdocs/using-cloud-storage.md @@ -281,12 +281,11 @@ techdocs: **3b. Authentication using app-config.yaml** -If you do not prefer (3a) and optionally like to use a service account, you can Set the config `techdocs.publisher.azureBlobStorage.credentials.accountName` in -follow these steps. your `app-config.yaml` to the your account name. +If you do not prefer (3a) and optionally like to use a service account, you can +follow these steps. To get credentials, access the Azure Portal and go to "Settings > Access Keys", and get your Storage account name and Primary Key. - https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key for more details. @@ -373,14 +372,14 @@ techdocs: $env: OPENSTACK_SWIFT_STORAGE_DOMAIN_ID domainName: $env: OPENSTACK_SWIFT_STORAGE_DOMAIN_NAME - region: + region: $env: OPENSTACK_SWIFT_STORAGE_REGION ``` **4. That's it!** -Your Backstage app is now ready to use OpenStack Swift Storage for TechDocs, to store -and read the static generated documentation files. When you start the backend of -the app, you should be able to see -`techdocs info Successfully connected to the OpenStack Swift Storage container` in -the logs. +Your Backstage app is now ready to use OpenStack Swift Storage for TechDocs, to +store and read the static generated documentation files. When you start the +backend of the app, you should be able to see +`techdocs info Successfully connected to the OpenStack Swift Storage container` +in the logs. diff --git a/packages/techdocs-common/__mocks__/pkgcloud.ts b/packages/techdocs-common/__mocks__/pkgcloud.ts index 4316630928..6b250092b8 100644 --- a/packages/techdocs-common/__mocks__/pkgcloud.ts +++ b/packages/techdocs-common/__mocks__/pkgcloud.ts @@ -21,7 +21,7 @@ import { EventEmitter } from 'events'; const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; const checkFileExists = async (Key: string): Promise => { - // Key will always have / as file separator irrespective of OS since S3 expects /. + // Key will always have / as file separator irrespective of OS since cloud providers expects /. // Normalize Key to OS specific path before checking if file exists. const filePath = path.join(rootDir, Key); diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts index 4c4c15d9c4..3987a36e17 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -49,7 +49,7 @@ export class OpenStackSwiftPublish implements PublisherBase { } catch (error) { throw new Error( "Since techdocs.publisher.type is set to 'openStackSwift' in your app config, " + - 'techdocs.publisher.openStackSwift.containerName is required.', + 'techdocs.publisher.openStackSwift.containerName is required.', ); } @@ -80,9 +80,9 @@ export class OpenStackSwiftPublish implements PublisherBase { } else { logger.error( `Could not retrieve metadata about the OpenStack Swift container ${containerName}. ` + - 'Make sure the container exists. Also make sure that authentication is setup either by ' + - 'explicitly defining credentials and region in techdocs.publisher.openStackSwift in app config or ' + - 'by using environment variables. Refer to https://backstage.io/docs/features/techdocs/using-cloud-storage', + 'Make sure the container exists. Also make sure that authentication is setup either by ' + + 'explicitly defining credentials and region in techdocs.publisher.openStackSwift in app config or ' + + 'by using environment variables. Refer to https://backstage.io/docs/features/techdocs/using-cloud-storage', ); logger.error(`from OpenStack client library: ${err.message}`); @@ -138,16 +138,17 @@ export class OpenStackSwiftPublish implements PublisherBase { }; // Rate limit the concurrent execution of file uploads to batches of 10 (per publish) - const uploadFile = limiter(() => - new Promise((res, rej) => { - const writeStream = this.storageClient.upload(params); + const uploadFile = limiter( + () => + new Promise((res, rej) => { + const writeStream = this.storageClient.upload(params); - writeStream.on('error', rej); + writeStream.on('error', rej); - writeStream.on('success', res); + writeStream.on('success', res); - readStream.pipe(writeStream); - }), + readStream.pipe(writeStream); + }), ); uploadPromises.push(uploadFile); } From 37586e0fc9b0e55c692d6d52a29f52afef71117b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Can=20Bilgi=C3=A7?= Date: Fri, 26 Feb 2021 15:26:28 +0300 Subject: [PATCH 15/17] runned prettier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- docs/features/techdocs/using-cloud-storage.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/features/techdocs/using-cloud-storage.md b/docs/features/techdocs/using-cloud-storage.md index 8b8ff04573..e6dcd077cd 100644 --- a/docs/features/techdocs/using-cloud-storage.md +++ b/docs/features/techdocs/using-cloud-storage.md @@ -257,7 +257,8 @@ techdocs: **3a. (Recommended) Authentication using environment variable** -Set the config `techdocs.publisher.azureBlobStorage.credentials.accountName` in +If you do not prefer (3a) and optionally like to use a service account, you can +set the config `techdocs.publisher.azureBlobStorage.credentials.accountName` in your `app-config.yaml` to the your account name. The storage blob client will automatically use the environment variable From aa095e469fa5f054c7c5757053edbb2cdf4364cb Mon Sep 17 00:00:00 2001 From: erdoganoksuz Date: Mon, 1 Mar 2021 11:20:17 +0300 Subject: [PATCH 16/17] pull request feedbacks fix. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- .changeset/nine-comics-smash.md | 7 +++++++ .changeset/odd-mirrors-smell.md | 7 ------- docs/features/techdocs/README.md | 1 + .../src/stages/publish/openStackSwift.ts | 11 +++++++---- plugins/techdocs/config.d.ts | 4 ++-- 5 files changed, 17 insertions(+), 13 deletions(-) create mode 100644 .changeset/nine-comics-smash.md delete mode 100644 .changeset/odd-mirrors-smell.md diff --git a/.changeset/nine-comics-smash.md b/.changeset/nine-comics-smash.md new file mode 100644 index 0000000000..147944930d --- /dev/null +++ b/.changeset/nine-comics-smash.md @@ -0,0 +1,7 @@ +--- +'@backstage/techdocs-common': patch +'@backstage/plugin-techdocs-backend': patch +'@backstage/plugin-techdocs': patch +--- + +OpenStack Swift publisher added for tech-docs. diff --git a/.changeset/odd-mirrors-smell.md b/.changeset/odd-mirrors-smell.md deleted file mode 100644 index 7b6e0285ef..0000000000 --- a/.changeset/odd-mirrors-smell.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/techdocs-common': minor -'@backstage/plugin-techdocs': minor -'@backstage/plugin-techdocs-backend': minor ---- - -OpenStack Swift Publisher added to TechDocs diff --git a/docs/features/techdocs/README.md b/docs/features/techdocs/README.md index 2fa48f57b0..7025364f78 100644 --- a/docs/features/techdocs/README.md +++ b/docs/features/techdocs/README.md @@ -54,6 +54,7 @@ providers are used. | Google Cloud Storage (GCS) | Yes ✅ | | Amazon Web Services (AWS) S3 | Yes ✅ | | Azure Blob Storage | Yes ✅ | +| OpenStack Swift | Yes ✅ | [Reach out to us](#feedback) if you want to request more platforms. diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts index 3987a36e17..0e60bc5796 100644 --- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts +++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts @@ -130,8 +130,6 @@ export class OpenStackSwiftPublish implements PublisherBase { const entityRootDir = `${entity.metadata.namespace}/${entity.kind}/${entity.metadata.name}`; const destination = `${entityRootDir}/${relativeFilePathPosix}`; // Swift container file relative path - const readStream = fs.createReadStream(filePath, 'utf8'); - const params = { container: this.containerName, remote: destination, @@ -141,6 +139,8 @@ export class OpenStackSwiftPublish implements PublisherBase { const uploadFile = limiter( () => new Promise((res, rej) => { + const readStream = fs.createReadStream(filePath, 'utf8'); + const writeStream = this.storageClient.upload(params); writeStream.on('error', rej); @@ -246,10 +246,13 @@ export class OpenStackSwiftPublish implements PublisherBase { this.storageClient.getFile( this.containerName, `${entityRootDir}/index.html`, - (err: any, file: any) => { + (err, file) => { if (!err && file) { res(true); - } else res(false); + } else { + res(false); + this.logger.warn(err.message); + } }, ); }); diff --git a/plugins/techdocs/config.d.ts b/plugins/techdocs/config.d.ts index 5e12f7dbc7..54d2b8fb69 100644 --- a/plugins/techdocs/config.d.ts +++ b/plugins/techdocs/config.d.ts @@ -100,12 +100,12 @@ export interface Config { credentials: { /** * (Required) Root user name - * @visibility backend + * @visibility secret */ username: string; /** * (Required) Root user password - * @visibility backend + * @visibility secret */ password: string; // required }; From 7bf88b4f311e0c6d1dc7ac234a02f77ce056433f Mon Sep 17 00:00:00 2001 From: erdoganoksuz Date: Tue, 2 Mar 2021 11:53:28 +0300 Subject: [PATCH 17/17] error name and documentation consistency. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mert Can Bilgiç --- docs/features/techdocs/using-cloud-storage.md | 4 ++-- packages/techdocs-common/__mocks__/pkgcloud.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/techdocs/using-cloud-storage.md b/docs/features/techdocs/using-cloud-storage.md index e6dcd077cd..fd8f320859 100644 --- a/docs/features/techdocs/using-cloud-storage.md +++ b/docs/features/techdocs/using-cloud-storage.md @@ -315,7 +315,7 @@ the logs. Follow the [official OpenStack Api documentation](https://docs.openstack.org/api-ref/identity/v3/) -for the latest instructions on the following steps involving Open Stack Storage. +for the latest instructions on the following steps involving OpenStack Storage. **1. Set `techdocs.publisher.type` config in your `app-config.yaml`** @@ -349,7 +349,7 @@ techdocs: **3. Authentication using app-config.yaml** -Set the configs in your `app-config.yaml` to the your container name. +Set the configs in your `app-config.yaml` to point to your container name. https://docs.openstack.org/api-ref/identity/v3/?expanded=password-authentication-with-unscoped-authorization-detail#password-authentication-with-unscoped-authorization for more details. diff --git a/packages/techdocs-common/__mocks__/pkgcloud.ts b/packages/techdocs-common/__mocks__/pkgcloud.ts index 6b250092b8..5d9f81cda0 100644 --- a/packages/techdocs-common/__mocks__/pkgcloud.ts +++ b/packages/techdocs-common/__mocks__/pkgcloud.ts @@ -54,7 +54,7 @@ class PkgCloudStorageClient { callback: (err: string, container: string) => any, ) { if (containerName !== 'mock') { - callback("Container doesn't exist", containerName); + callback('Container does not exist', containerName); throw new Error('Container does not exist'); } else { callback('Container does not exist', 'success');