diff --git a/.changeset/forty-gorillas-help.md b/.changeset/forty-gorillas-help.md new file mode 100644 index 0000000000..3f185525a2 --- /dev/null +++ b/.changeset/forty-gorillas-help.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Minor visual tweaks to adapt to changes in mkdocs-material v9 diff --git a/.changeset/new-pigs-jam.md b/.changeset/new-pigs-jam.md new file mode 100644 index 0000000000..efea0076d2 --- /dev/null +++ b/.changeset/new-pigs-jam.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': minor +--- + +Created `confluence:transform:markdown` action for converting confluence docs to Markdown. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a38d085dc9..b4be92e4ec 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,14 +8,14 @@ yarn.lock @backstage/maintainers @backstage-service */yarn.lock @backstage/maintainers @backstage-service /.changeset/*.md -/cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-core -/docs/assets/search @backstage/techdocs-core -/docs/features/search @backstage/techdocs-core -/docs/features/techdocs @backstage/techdocs-core -/docs/plugins/integrating-search-into-plugins.md @backstage/techdocs-core +/cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-maintainers +/docs/assets/search @backstage/discoverability-maintainers +/docs/features/search @backstage/discoverability-maintainers +/docs/features/techdocs @backstage/techdocs-maintainers +/docs/plugins/integrating-search-into-plugins.md @backstage/discoverability-maintainers /packages/cli/src/commands/onboard @backstage/sharks -/packages/techdocs-cli @backstage/techdocs-core -/packages/techdocs-cli-embedded-app @backstage/techdocs-core +/packages/techdocs-cli @backstage/techdocs-maintainers +/packages/techdocs-cli-embedded-app @backstage/techdocs-maintainers /plugins/adr @backstage/maintainers @kuangp /plugins/adr-* @backstage/maintainers @kuangp /plugins/allure @backstage/maintainers @deepak-bhardwaj-ps @@ -55,7 +55,7 @@ yarn.lock @backstage/maintainers @back /plugins/fossa @backstage/maintainers @backstage/sda-se-reviewers /plugins/gcalendar @backstage/maintainers @szubster @ptychu @kielosz @alexrybch /plugins/git-release-manager @backstage/maintainers @erikengervall -/plugins/home @backstage/techdocs-core +/plugins/home @backstage/discoverability-maintainers /plugins/ilert @backstage/maintainers @yacut /plugins/jenkins @backstage/maintainers @timja /plugins/jenkins-backend @backstage/maintainers @timja @@ -71,13 +71,13 @@ yarn.lock @backstage/maintainers @back /plugins/rollbar-backend @backstage/maintainers @andrewthauer /plugins/scaffolder-backend-module-rails @backstage/maintainers @angeliski /plugins/scaffolder-backend-module-yeoman @backstage/maintainers @pawelmitka -/plugins/search @backstage/techdocs-core -/plugins/search-* @backstage/techdocs-core +/plugins/search @backstage/discoverability-maintainers +/plugins/search-* @backstage/discoverability-maintainers /plugins/sonarqube @backstage/maintainers @backstage/sda-se-reviewers -/plugins/stack-overflow @backstage/techdocs-core -/plugins/stack-overflow-backend @backstage/techdocs-core -/plugins/techdocs @backstage/techdocs-core -/plugins/techdocs-* @backstage/techdocs-core +/plugins/stack-overflow @backstage/discoverability-maintainers +/plugins/stack-overflow-backend @backstage/discoverability-maintainers +/plugins/techdocs @backstage/techdocs-maintainers +/plugins/techdocs-* @backstage/techdocs-maintainers /plugins/user-settings-backend @backstage/maintainers @backstage/sda-se-reviewers /tech-insights-backend @backstage/maintainers @xantier @iain-b /tech-insights-backend-module-jsonfc @backstage/maintainers @xantier @iain-b diff --git a/OWNERS.md b/OWNERS.md index 1834abe227..08736d5723 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -24,6 +24,20 @@ Team: @backstage/catalog-maintainers | ---- | ------------ | --------- | ---------------------------- | ------- | | TBD | Spotify | Chipmunks | [TBD](http://github.com/TBD) | - | +### Discoverability + +Team: @backstage/discoverability-maintainers + +Scope: Discoverability within Backstage, including the home page, information architecture, and search + +| Name | Organization | Team | GitHub | Discord | +| ------------------------ | ------------ | ---- | ---------------------------------------- | ------------------ | +| Avantika Iyer | Spotify | BUX | [tikabom](http://github.com/tikabom) | - | +| Camila Belo | Spotify | BUX | [camilaibs](http://github.com/camilaibs) | Camila Loiola#0226 | +| Emma Indal | Spotify | BUX | [emmaindal](http://github.com/emmaindal) | emmaindal#7503 | +| Raghunandan Balachandran | Spotify | BUX | [soapraj](http://github.com/soapraj) | raghunandanb#1114 | +| Renan Mendes Carvalho | Spotify | BUX | [aitherios](http://github.com/aitherios) | aitherios#0593 | + ### Kubernetes Team: @backstage/kubernetes-maintainers @@ -38,17 +52,12 @@ Scope: The Kubernetes plugin and the base it provides for other plugins to build Team: @backstage/techdocs-maintainers -| Name | Organization | Team | GitHub | Discord | -| ---- | ------------ | ------------ | ---------------------------- | ------- | -| TBD | Spotify | Pulp Fiction | [TBD](http://github.com/TBD) | - | +Scope: The TechDocs plugin and related tooling -### Search - -Team: @backstage/search-maintainers - -| Name | Organization | Team | GitHub | Discord | -| ---- | ------------ | ---- | ---------------------------- | ------- | -| TBD | Spotify | BUX | [TBD](http://github.com/TBD) | - | +| Name | Organization | Team | GitHub | Discord | +| ------------------------ | ------------ | ------------ | ------------------------------------------------ | ------------------ | +| Morgan Bentell | Spotify | Pulp Fiction | [agentbellnorm](http://github.com/agentbellnorm) | morganbentell#9030 | +| Raghunandan Balachandran | Spotify | Pulp Fiction | [soapraj](http://github.com/soapraj) | raghunandanb#1114 | ## Sponsors @@ -60,17 +69,20 @@ Team: @backstage/search-maintainers ## Organization Members -| Name | Organization | GitHub | Discord | -| --------------- | ------------------------ | ----------------------------------------------- | ------------------------------ | -| Adam Harvey | Cisco | [adamdmharvey](https://github.com/adamdmharvey) | `adamharvey#3739` | -| Andre Wanlin | Keyloop | [awanlin](https://github.com/awanlin) | `Ahhhndre#3095` | -| Andrew Thauer | Wealthsimple | [andrewthauer](https://github.com/andrewthauer) | `andrewthauer#3060` | -| Brian Fletcher | RoadieHQ | [punkle](https://github.com/punkle) | `Brian Fletcher#7051` | -| David Tuite | Roadie | [dtuite](https://github.com/dtuite) | `David Tuite (roadie.io)#1010` | -| Jussi Hallila | Roadie | [Xantier](https://github.com/Xantier) | `Xantier#0086` | -| Mark Avery | Cvent | [webark](https://github.com/webark) | `webark#8471` | -| Phil Kuang | FactSet Research Systems | [kuangp](https://github.com/kuangp) | `pkuang#3202` | -| Taras Mankovski | Frontside | [taras](https://github.com/taras) | `tarasm#1256` | +| Name | Organization | GitHub | Discord | +| ------------------ | ------------------------- | ----------------------------------------------- | ------------------------------ | +| Adam Harvey | Cisco | [adamdmharvey](https://github.com/adamdmharvey) | `adamharvey#3739` | +| Andre Wanlin | Keyloop | [awanlin](https://github.com/awanlin) | `Ahhhndre#3095` | +| Andrew Thauer | Wealthsimple | [andrewthauer](https://github.com/andrewthauer) | `andrewthauer#3060` | +| Brian Fletcher | RoadieHQ | [punkle](https://github.com/punkle) | `Brian Fletcher#7051` | +| David Tuite | Roadie | [dtuite](https://github.com/dtuite) | `David Tuite (roadie.io)#1010` | +| Himanshu Mishra | Harness.io | [OrkoHunter](https://github.com/OrkoHunter) | `OrkoHunter#1520` | +| Jamie Klassen | VMware | [jamieklassen](https://github.com/jamieklassen) | `jamieklassen#3047` | +| Jussi Hallila | Roadie | [Xantier](https://github.com/Xantier) | `Xantier#0086` | +| Mark Avery | Cvent | [webark](https://github.com/webark) | `webark#8471` | +| Patrick Jungermann | Bonial International GmbH | [pjungermann](https://github.com/pjungermann) | `pjungermann#6933` | +| Phil Kuang | FactSet Research Systems | [kuangp](https://github.com/kuangp) | `pkuang#3202` | +| Taras Mankovski | Frontside | [taras](https://github.com/taras) | `tarasm#1256` | ## Emeritus Core Maintainers diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 38067f5214..689171ffa9 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -9438,15 +9438,15 @@ __metadata: linkType: hard "sass@npm:^1.57.1": - version: 1.59.3 - resolution: "sass@npm:1.59.3" + version: 1.60.0 + resolution: "sass@npm:1.60.0" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 839b5282cdf7d0ba3fdbfb605277dd584a8c40fa3e3e58ad905d64cd812acfb82ff0a4072d4981673db884ee61505472ff07c5c5a8a497f16ba013b183ba6473 + checksum: 06e163c37af466ec194cf2ed8dab616372afeb19322d356947d48ea664fc38398ae77c4d91bea9cb0ace954ce289d5518d0f8555ecc49f6bf2539a8ef52fc580 languageName: node linkType: hard diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/.eslintrc.js b/plugins/scaffolder-backend-module-confluence-to-markdown/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/README.md b/plugins/scaffolder-backend-module-confluence-to-markdown/README.md new file mode 100644 index 0000000000..d9b235d6c1 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/README.md @@ -0,0 +1,118 @@ +# @backstage/plugin-scaffolder-backend-module-confluence-to-markdown + +Welcome to the `confluence:transform:markdown` action for the `scaffolder-backend`. + +## Getting started + +You need to configure the action in your backend: + +## From your Backstage root directory + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +``` + +Configure the action: +(you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options): + +```typescript +// packages/backend/src/plugins/scaffolder.ts + +import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; +import { ScmIntegrations } from '@backstage/integration'; +import { createConfluenceToMarkdownAction } from '@backstage/plugin-scaffolder-backend-module-confluence-to-markdown'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + const catalogClient = new CatalogClient({ discoveryApi: env.discovery }); + const integrations = ScmIntegrations.fromConfig(env.config); + + const builtInActions = createBuiltinActions({ + integrations, + catalogClient, + config: env.config, + reader: env.reader, + }); + + const actions = [ + ...builtInActions, + createConfluenceToMarkdownAction({ + integrations, + config: env.config, + reader: env.reader, + }), + ]; + + return createRouter({ + actions, + catalogClient: catalogClient, + logger: env.logger, + config: env.config, + database: env.database, + reader: env.reader, + }); +} +``` + +You will also need an access token for authorization with `Read` permissions. You can create a Personal Access Token (PAT) in confluence and add the PAT to your `app-config.yaml` + +```yaml +confluence: + baseUrl: ${CONFLUENCE_BASE_URL} + token: ${CONFLUENCE_TOKEN} +``` + +After that you can use the action in your template: + +```yaml +apiVersion: scaffolder.backstage.io/v1beta3 +kind: Template +metadata: + name: confluence-to-markdown + title: Confluence to Markdown + description: This template converts a single confluence document to Markdown for Techdocs and adds it to a given GitHub repo. + tags: + - do-not-use + - poc +spec: + owner: + type: service + parameters: + - title: Confluence and Github Repo Information + properties: + confluenceUrls: + type: array + description: Urls for confluence doc to be converted to markdown. In format /display// + items: + type: string + default: confluence url + ui:options: + addable: true + minItems: 1 + maxItems: 5 + repoUrl: + type: string + title: GitHub URL mkdocs.yaml link + description: The GitHub repo URL to your mkdocs.yaml file. Example + steps: + - id: create-docs + name: Get markdown file created and update markdown.yaml file + action: confluence:transform:markdown + input: + confluenceUrls: ${{ parameters.confluenceUrls }} + repoUrl: ${{ parameters.repoUrl }} + - id: publish + name: Publish PR to GitHub + action: publish:github:pull-request + input: + repoUrl: ?repo=${{ steps['create-docs'].output.repo }}&owner=${{ steps['create-docs'].output.owner }} + branchName: confluence-to-markdown + title: Confluence to Markdown + description: PR for converting confluence page to mkdocs +``` + +Replace `` with your GitHub URL without `https://`. + +You can find a list of all registered actions including their parameters at the /create/actions route in your Backstage application. diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/api-report.md b/plugins/scaffolder-backend-module-confluence-to-markdown/api-report.md new file mode 100644 index 0000000000..398beb5059 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/api-report.md @@ -0,0 +1,20 @@ +## API Report File for "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { Config } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import { TemplateAction } from '@backstage/plugin-scaffolder-node'; +import { UrlReader } from '@backstage/backend-common'; + +// @public (undocumented) +export const createConfluenceToMarkdownAction: (options: { + reader: UrlReader; + integrations: ScmIntegrations; + config: Config; +}) => TemplateAction<{ + confluenceUrls: string[]; + repoUrl: string; +}>; +``` diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json new file mode 100644 index 0000000000..7ad5f127f6 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -0,0 +1,49 @@ +{ + "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", + "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "help": "backstage-cli help" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-scaffolder-backend": "workspace:^", + "@backstage/plugin-scaffolder-node": "workspace:^", + "@backstage/types": "workspace:^", + "fs-extra": "10.1.0", + "git-url-parse": "^13.1.0", + "node-fetch": "^2.6.7", + "node-html-markdown": "^1.3.0", + "yaml": "^2.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/test-utils": "workspace:^", + "mock-fs": "^5.2.0", + "msw": "^1.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.test.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.test.ts new file mode 100644 index 0000000000..b95d8ae520 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.test.ts @@ -0,0 +1,294 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { PassThrough } from 'stream'; +import { createConfluenceToMarkdownAction } from './confluenceToMarkdown'; +import { getVoidLogger } from '@backstage/backend-common'; +import { UrlReader } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import { setupRequestMockHandlers } from '@backstage/test-utils'; +import mockFs from 'mock-fs'; +import os from 'os'; +import type { ActionContext } from '@backstage/plugin-scaffolder-node'; +import { readFile, writeFile, createWriteStream } from 'fs-extra'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; + +jest.mock('fs-extra', () => ({ + mkdirSync: jest.fn(), + readFile: jest.fn().mockResolvedValue('File contents'), + writeFile: jest.fn().mockImplementation(() => { + return Promise.resolve(); + }), + outputFile: jest.fn(), + openSync: jest.fn(), + createWriteStream: jest.fn().mockReturnValue(new PassThrough()), + ensureDir: jest.fn(), +})); + +describe('confluence:transform:markdown', () => { + const baseUrl = `https://nodomain.confluence.com`; + const worker = setupServer(); + setupRequestMockHandlers(worker); + + const config = new ConfigReader({ + confluence: { + baseUrl: baseUrl, + token: 'fake_token', + }, + }); + + const integrations = ScmIntegrations.fromConfig( + new ConfigReader({ + integrations: { + github: [{ host: 'github.com', token: 'token' }], + }, + }), + ); + + let reader: UrlReader; + let mockContext: ActionContext<{ + confluenceUrls: string[]; + repoUrl: string; + }>; + + const logger = getVoidLogger(); + jest.spyOn(logger, 'info'); + + const mockTmpDir = os.tmpdir(); + + beforeEach(() => { + reader = { + readUrl: jest.fn(), + readTree: jest.fn().mockResolvedValue({ + dir: jest.fn(), + }), + search: jest.fn(), + }; + mockContext = { + input: { + confluenceUrls: [ + 'https://nodomain.confluence.com/display/testing/mkdocs', + ], + repoUrl: 'https://notreal.github.com/space/backstage/mkdocs.yml', + }, + workspacePath: '/tmp', + logger, + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir), + }; + mockFs({ [`${mockTmpDir}/src/docs`]: {} }); + }); + afterEach(() => { + jest.clearAllMocks(); + mockFs.restore(); + }); + + it('should call confluence to markdown action successfully with results array', async () => { + const options = { + reader, + integrations, + config, + }; + const responseBody = { + results: [ + { + id: '4444444', + type: 'page', + title: 'Testing', + body: { + export_view: { + value: '

hello world

', + }, + }, + }, + ], + }; + const responseBodyTwo = { + results: [ + { + id: '4444444', + type: 'attachment', + title: 'testing.pdf', + metadata: { + mediaType: 'application/pdf', + }, + _links: { + download: '/download/attachments/4444444/testing.pdf', + }, + }, + ], + }; + + worker.use( + rest.get(`${baseUrl}/rest/api/content`, (_, res, ctx) => + res(ctx.status(200, 'OK'), ctx.json(responseBody)), + ), + rest.get( + `${baseUrl}/rest/api/content/4444444/child/attachment`, + (_, res, ctx) => res(ctx.status(200, 'OK'), ctx.json(responseBodyTwo)), + ), + rest.get( + `${baseUrl}/download/attachments/4444444/testing.pdf`, + (_, res, ctx) => res(ctx.status(200, 'OK'), ctx.body('hello')), + ), + ); + + const action = createConfluenceToMarkdownAction(options); + + await action.handler(mockContext); + + expect(logger.info).toHaveBeenCalledWith( + `Fetching the mkdocs.yml catalog from https://notreal.github.com/space/backstage/mkdocs.yml`, + ); + expect(logger.info).toHaveBeenCalledTimes(5); + expect(createWriteStream).toHaveBeenCalledTimes(1); + expect(readFile).toHaveBeenCalledTimes(1); + expect(writeFile).toHaveBeenCalledTimes(1); + }); + + it('should call confluence to markdown action successfully with empty results array', async () => { + const options = { + reader, + integrations, + config, + }; + const responseBody = { + results: [ + { + id: '4444444', + type: 'page', + title: 'Testing', + body: { + export_view: { + value: '

hello world

', + }, + }, + }, + ], + }; + const responseBodyTwo = { + results: [], + }; + + worker.use( + rest.get(`${baseUrl}/rest/api/content`, (_, res, ctx) => + res(ctx.status(200, 'OK'), ctx.json(responseBody)), + ), + rest.get( + `${baseUrl}/rest/api/content/4444444/child/attachment`, + (_, res, ctx) => res(ctx.status(200, 'OK'), ctx.json(responseBodyTwo)), + ), + ); + + const action = createConfluenceToMarkdownAction(options); + + await action.handler(mockContext); + + expect(logger.info).toHaveBeenCalledWith( + `Fetching the mkdocs.yml catalog from https://notreal.github.com/space/backstage/mkdocs.yml`, + ); + expect(logger.info).toHaveBeenCalledTimes(5); + + expect(createWriteStream).not.toHaveBeenCalled(); + expect(readFile).toHaveBeenCalledTimes(1); + expect(writeFile).toHaveBeenCalledTimes(1); + }); + + it('shoud fail on the first fetch call with response.ok set to false', async () => { + const options = { + reader, + integrations, + config, + }; + const responseBody = {}; + + worker.use( + rest.get(`${baseUrl}/rest/api/content`, (_, res, ctx) => + res(ctx.status(401, 'nope'), ctx.json(responseBody)), + ), + ); + + const action = createConfluenceToMarkdownAction(options); + await expect(async () => { + await action.handler(mockContext); + }).rejects.toThrow('Request failed with 401 Error'); + }); + + it('should return nothing in results from the first api call and fail', async () => { + const options = { + reader, + integrations, + config, + }; + const responseBody = { + results: [], + }; + + worker.use( + rest.get(`${baseUrl}/rest/api/content`, (_, res, ctx) => + res(ctx.status(200, 'OK'), ctx.json(responseBody)), + ), + ); + + const action = createConfluenceToMarkdownAction(options); + await expect(async () => { + await action.handler(mockContext); + }).rejects.toThrow( + 'Could not find document https://nodomain.confluence.com/display/testing/mkdocs. Please check your input.', + ); + }); + + it('should fail on the second fetch call to confluence', async () => { + const options = { + reader, + integrations, + config, + }; + const responseBody = { + results: [ + { + id: '4444444', + type: 'page', + title: 'Testing', + body: { + export_view: { + value: '

hello world

', + }, + }, + }, + ], + }; + const responseBodyTwo = {}; + + worker.use( + rest.get(`${baseUrl}/rest/api/content`, (_, res, ctx) => + res(ctx.status(200, 'OK'), ctx.json(responseBody)), + ), + rest.get( + `${baseUrl}/rest/api/content/4444444/child/attachment`, + (_, res, ctx) => + res(ctx.status(404, 'nope'), ctx.json(responseBodyTwo)), + ), + ); + + const action = createConfluenceToMarkdownAction(options); + await expect(async () => { + await action.handler(mockContext); + }).rejects.toThrow('Request failed with 404 Error'); + }); +}); diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts new file mode 100644 index 0000000000..655b1bbdb7 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts @@ -0,0 +1,182 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { Config } from '@backstage/config'; +import { UrlReader } from '@backstage/backend-common'; +import { ScmIntegrations } from '@backstage/integration'; +import { createFetchPlainAction } from '@backstage/plugin-scaffolder-backend'; +import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { InputError, ConflictError } from '@backstage/errors'; +import { NodeHtmlMarkdown } from 'node-html-markdown'; +import fs from 'fs-extra'; +import parseGitUrl from 'git-url-parse'; +import YAML from 'yaml'; +import { + readFileAsString, + fetchConfluence, + getAndWriteAttachments, + createConfluenceVariables, +} from './helpers'; + +/** + * @public + */ + +export const createConfluenceToMarkdownAction = (options: { + reader: UrlReader; + integrations: ScmIntegrations; + config: Config; +}) => { + const { config, reader, integrations } = options; + const fetchPlainAction = createFetchPlainAction({ reader, integrations }); + type Obj = { + [key: string]: string; + }; + + return createTemplateAction<{ + confluenceUrls: string[]; + repoUrl: string; + }>({ + id: 'confluence:transform:markdown', + schema: { + input: { + properties: { + confluenceUrls: { + type: 'array', + title: 'Confluence URL', + description: + 'Paste your confluence url. Ensure it follows this format: https://{confluence+base+url}/display/{spacekey}/{page+title}', + items: { + type: 'string', + default: 'Confluence URL', + }, + }, + repoUrl: { + type: 'string', + title: 'GitHub Repo Url', + description: + 'mkdocs.yml file location inside the github repo you want to store the document', + }, + }, + }, + }, + async handler(ctx) { + const { confluenceUrls, repoUrl } = ctx.input; + const parsedRepoUrl = parseGitUrl(repoUrl); + const filePathToMkdocs = parsedRepoUrl.filepath.substring( + 0, + parsedRepoUrl.filepath.lastIndexOf('/') + 1, + ); + const dirPath = ctx.workspacePath; + let productArray: string[][] = []; + + ctx.logger.info(`Fetching the mkdocs.yml catalog from ${repoUrl}`); + + // This grabs the files from Github + const repoFileDir = `${dirPath}/${parsedRepoUrl.filepath}`; + await fetchPlainAction.handler({ + ...ctx, + input: { + url: `https://${parsedRepoUrl.resource}/${parsedRepoUrl.owner}/${parsedRepoUrl.name}`, + targetPath: dirPath, + }, + }); + + for (const url of confluenceUrls) { + const { spacekey, title, titleWithSpaces } = + await createConfluenceVariables(url); + // This calls confluence to get the page html and page id + const getConfluenceDoc = await fetchConfluence( + `/rest/api/content?title=${title}&spaceKey=${spacekey}&expand=body.export_view`, + config, + ); + if (getConfluenceDoc.results.length === 0) { + throw new InputError( + `Could not find document ${url}. Please check your input.`, + ); + } + // This gets attachements for the confluence page if they exist + const getDocAttachments = await fetchConfluence( + `/rest/api/content/${getConfluenceDoc.results[0].id}/child/attachment`, + config, + ); + + if (getDocAttachments.results.length) { + fs.mkdirSync(`${dirPath}/${filePathToMkdocs}docs/img`, { + recursive: true, + }); + productArray = await getAndWriteAttachments( + getDocAttachments, + dirPath, + config, + filePathToMkdocs, + ); + } + + ctx.logger.info( + `starting action for converting ${titleWithSpaces} from Confluence To Markdown`, + ); + + // This reads mkdocs.yml file + const mkdocsFileContent = await readFileAsString(repoFileDir); + const mkdocsFile = await YAML.parse(mkdocsFileContent); + ctx.logger.info( + `Adding new file - ${titleWithSpaces} to the current mkdocs.yml file`, + ); + + // This modifies the mkdocs.yml file + if (mkdocsFile !== undefined && mkdocsFile.hasOwnProperty('nav')) { + const { nav } = mkdocsFile; + if (!nav.some((i: Obj) => i.hasOwnProperty(titleWithSpaces))) { + nav.push({ + [titleWithSpaces]: `${titleWithSpaces.replace(/\s+/g, '-')}.md`, + }); + mkdocsFile.nav = nav; + } else { + throw new ConflictError( + 'This document looks to exist inside the GitHub repo. Will end the action.', + ); + } + } + + await fs.writeFile(repoFileDir, YAML.stringify(mkdocsFile)); + + // This grabs the confluence html and converts it to markdown and adds attachments + const html = getConfluenceDoc.results[0].body.export_view.value; + const markdownToPublish = NodeHtmlMarkdown.translate(html); + let newString: string = markdownToPublish; + productArray.forEach((product: string[]) => { + // This regex is looking for either [](link to confluence) or ![](link to confluence) in the newly created markdown doc and updating it to point to the versions saved(in ./docs/img) in the local version of GitHub Repo during getAndWriteAttachments + const regex = product[0].includes('.pdf') + ? new RegExp(`(\\[.*?\\]\\()(.*?${product[0]}.*?)(\\))`, 'gi') + : new RegExp(`(\\!\\[.*?\\]\\()(.*?${product[0]}.*?)(\\))`, 'gi'); + newString = newString.replace(regex, `$1./img/${product[1]}$3`); + }); + + ctx.logger.info(`Adding new file to repo.`); + await fs.outputFile( + `${dirPath}/${filePathToMkdocs}docs/${titleWithSpaces.replace( + /\s+/g, + '-', + )}.md`, + newString, + ); + } + + ctx.output('repo', parsedRepoUrl.name); + ctx.output('owner', parsedRepoUrl.owner); + }, + }); +}; diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.ts new file mode 100644 index 0000000000..58f84a8747 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.ts @@ -0,0 +1,128 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { Config } from '@backstage/config'; +import { ResponseError, ConflictError, InputError } from '@backstage/errors'; +import fs from 'fs-extra'; +import fetch, { Response } from 'node-fetch'; + +interface Links { + webui: string; + download: string; + thumbnail: string; + self: string; +} + +interface Metadata { + mediaType: string; +} + +export interface Result { + id: string; + type: string; + status: string; + title: string; + metadata: Metadata; + _links: Links; +} + +export interface Results { + results: Result[]; +} + +export const readFileAsString = async (fileDir: string) => { + const content = await fs.readFile(fileDir, 'utf-8'); + return content.toString(); +}; + +export const fetchConfluence = async (relativeUrl: string, config: Config) => { + const baseUrl = config.getString('confluence.baseUrl'); + const token = config.getString('confluence.token'); + const response: Response = await fetch(`${baseUrl}${relativeUrl}`, { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + }, + }); + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + return response.json(); +}; + +export const getAndWriteAttachments = async ( + arr: Results, + workspace: string, + config: Config, + mkdocsDir: string, +) => { + const productArr: string[][] = []; + const baseUrl = config.getString('confluence.baseUrl'); + const token = config.getString('confluence.token'); + await Promise.all( + await arr.results.map(async (result: Result) => { + const downloadLink = result._links.download; + const downloadTitle = result.title.replace(/ /g, '-'); + if (result.metadata.mediaType !== 'application/gliffy+json') { + productArr.push([result.title.replace(/ /g, '%20'), downloadTitle]); + } + + const res = await fetch(`${baseUrl}${downloadLink}`, { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + }, + }); + if (!res.ok) { + throw ResponseError.fromResponse(res); + } else if (res.body !== null) { + fs.openSync(`${workspace}/${mkdocsDir}docs/img/${downloadTitle}`, 'w'); + const writeStream = fs.createWriteStream( + `${workspace}/${mkdocsDir}docs/img/${downloadTitle}`, + ); + res.body.pipe(writeStream); + await new Promise((resolve, reject) => { + writeStream.on('finish', () => { + resolve(`${workspace}/${mkdocsDir}docs/img/${downloadTitle}`); + }); + writeStream.on('error', reject); + }); + } else { + throw new ConflictError( + 'No Body on the response. Can not save images from Confluence Doc', + ); + } + }), + ); + return productArr; +}; + +export const createConfluenceVariables = async (url: string) => { + let spacekey: string | undefined = undefined; + let title: string | undefined = undefined; + let titleWithSpaces: string | undefined = ''; + const params = new URL(url); + if (params.pathname.split('/')[1] === 'display') { + spacekey = params.pathname.split('/')[2]; + title = params.pathname.split('/')[3]; + titleWithSpaces = title?.replace(/\+/g, ' '); + return { spacekey, title, titleWithSpaces }; + } + throw new InputError( + 'The Url format for Confluence is incorrect. Acceptable format is `/display//`', + ); +}; diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/index.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/index.ts new file mode 100644 index 0000000000..00f00eabda --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { createConfluenceToMarkdownAction } from './confluenceToMarkdown'; diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/index.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/index.ts new file mode 100644 index 0000000000..3d3a6c73a0 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './confluence'; diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/index.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/index.ts new file mode 100644 index 0000000000..80d2e35cb3 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ + +/** + * The confluence-to-markdown module for \@backstage/plugin-scaffolder-backend. + * + * @packageDocumentation + */ + +export * from './actions'; diff --git a/plugins/techdocs/src/reader/transformers/copyToClipboard.tsx b/plugins/techdocs/src/reader/transformers/copyToClipboard.tsx index 422bbb10ce..28a6c789b6 100644 --- a/plugins/techdocs/src/reader/transformers/copyToClipboard.tsx +++ b/plugins/techdocs/src/reader/transformers/copyToClipboard.tsx @@ -23,7 +23,7 @@ import { SvgIcon, Tooltip, } from '@material-ui/core'; -import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; import type { Transformer } from './transformer'; import useCopyToClipboard from 'react-use/lib/useCopyToClipboard'; @@ -69,9 +69,13 @@ const CopyToClipboardButton = ({ text }: CopyToClipboardButtonProps) => { onClose={handleClose} leaveDelay={1000} > - + ); }; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts b/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts index 979a616f01..c5e69e6084 100644 --- a/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts +++ b/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts @@ -21,6 +21,11 @@ const SIDEBAR_WIDTH = '224px'; export default ({ theme, sidebar }: RuleOptions) => ` /*================== Layout ==================*/ +/* mkdocs material v9 compat */ +.md-nav__title { + color: var(--md-default-fg-color); +} + .md-grid { max-width: 100%; margin: 0; diff --git a/yarn.lock b/yarn.lock index 88026d2897..3f5fbf684b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -518,15 +518,15 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/client-cognito-identity@npm:3.296.0": - version: 3.296.0 - resolution: "@aws-sdk/client-cognito-identity@npm:3.296.0" +"@aws-sdk/client-cognito-identity@npm:3.298.0": + version: 3.298.0 + resolution: "@aws-sdk/client-cognito-identity@npm:3.298.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.296.0 + "@aws-sdk/client-sts": 3.298.0 "@aws-sdk/config-resolver": 3.296.0 - "@aws-sdk/credential-provider-node": 3.296.0 + "@aws-sdk/credential-provider-node": 3.298.0 "@aws-sdk/fetch-http-handler": 3.296.0 "@aws-sdk/hash-node": 3.296.0 "@aws-sdk/invalid-dependency": 3.296.0 @@ -557,20 +557,20 @@ __metadata: "@aws-sdk/util-user-agent-node": 3.296.0 "@aws-sdk/util-utf8": 3.295.0 tslib: ^2.5.0 - checksum: 72c8aac879cc31f156178ecef5895b1c5c802fe1433a3ba0c0010364259815b7e96f7ca7872781e0a512f15ecacb82f62d67902d15f3960a4c0ebf20a1dfd3bd + checksum: 92ec17e7bfa2f71e06a916311ae624e3973b67d0ab1bd0ba7f27481df5cf34f0ee48d26b34bb0c328c7cc0adfd75e4822da0d186fd95cc99a7ac50b165751721 languageName: node linkType: hard "@aws-sdk/client-s3@npm:^3.208.0": - version: 3.296.0 - resolution: "@aws-sdk/client-s3@npm:3.296.0" + version: 3.298.0 + resolution: "@aws-sdk/client-s3@npm:3.298.0" dependencies: "@aws-crypto/sha1-browser": 3.0.0 "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.296.0 + "@aws-sdk/client-sts": 3.298.0 "@aws-sdk/config-resolver": 3.296.0 - "@aws-sdk/credential-provider-node": 3.296.0 + "@aws-sdk/credential-provider-node": 3.298.0 "@aws-sdk/eventstream-serde-browser": 3.296.0 "@aws-sdk/eventstream-serde-config-resolver": 3.296.0 "@aws-sdk/eventstream-serde-node": 3.296.0 @@ -619,19 +619,19 @@ __metadata: "@aws-sdk/xml-builder": 3.295.0 fast-xml-parser: 4.1.2 tslib: ^2.5.0 - checksum: e7c73260ec654780be882eddad68f39873010a436477c1536ae04abc249ba08280f240a8dea51db0e222fd623db12a5e555514b2dde9f042299643c483b446cd + checksum: 83d5876eb92493f64fc64dc49229b33f44f7f8def6fa9b32f42e341d0cbe50a340daaa9cbf4e7132e8271fd2f0c07a0a9a790249da3b34e54856d5003d3134bb languageName: node linkType: hard "@aws-sdk/client-sqs@npm:^3.208.0": - version: 3.296.0 - resolution: "@aws-sdk/client-sqs@npm:3.296.0" + version: 3.298.0 + resolution: "@aws-sdk/client-sqs@npm:3.298.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.296.0 + "@aws-sdk/client-sts": 3.298.0 "@aws-sdk/config-resolver": 3.296.0 - "@aws-sdk/credential-provider-node": 3.296.0 + "@aws-sdk/credential-provider-node": 3.298.0 "@aws-sdk/fetch-http-handler": 3.296.0 "@aws-sdk/hash-node": 3.296.0 "@aws-sdk/invalid-dependency": 3.296.0 @@ -665,13 +665,13 @@ __metadata: "@aws-sdk/util-utf8": 3.295.0 fast-xml-parser: 4.1.2 tslib: ^2.5.0 - checksum: ec3c7844ef114e8784efad036f0442bbc1a17bec5878fc22a4641bde3847a57c16e8d5ec92917df9f8a3601b63277852788b16727e9039d389cb8fcf06131e25 + checksum: 262b174e0c7df07dcc3eaf2741596caa7b2f9e964421e6e16102b55d11292c8c626061bf6e39175b80b19083f0e593302e7bb5328dbff80ea92b3aa50cb619c8 languageName: node linkType: hard -"@aws-sdk/client-sso-oidc@npm:3.296.0": - version: 3.296.0 - resolution: "@aws-sdk/client-sso-oidc@npm:3.296.0" +"@aws-sdk/client-sso-oidc@npm:3.298.0": + version: 3.298.0 + resolution: "@aws-sdk/client-sso-oidc@npm:3.298.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 @@ -705,13 +705,13 @@ __metadata: "@aws-sdk/util-user-agent-node": 3.296.0 "@aws-sdk/util-utf8": 3.295.0 tslib: ^2.5.0 - checksum: e2cae6747b8300a143e67354913964e2aa7d96fcb7d68e1020cbb40e2e9079a3aea10f427a3cddd98cf8e8463b189d1a15639d713e92d3dff2f8aada7527db9d + checksum: 48786eb7287a7430bd0460d6faf76903aa8979248d626da53727a20b2e4499d5b01e9ebc0206a7f4061673796c07d597d3fc52bd157d765483a59a5e7a484292 languageName: node linkType: hard -"@aws-sdk/client-sso@npm:3.296.0": - version: 3.296.0 - resolution: "@aws-sdk/client-sso@npm:3.296.0" +"@aws-sdk/client-sso@npm:3.298.0": + version: 3.298.0 + resolution: "@aws-sdk/client-sso@npm:3.298.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 @@ -745,18 +745,18 @@ __metadata: "@aws-sdk/util-user-agent-node": 3.296.0 "@aws-sdk/util-utf8": 3.295.0 tslib: ^2.5.0 - checksum: def371cedc2add3127d648c24ea1356517cba510f06679134add036f22c9d139205071fc30e0c753081507881068987efcef2a3a61ca283c4696614236febc08 + checksum: 226b8c211e78a384db7106c6c5ed4d8ae7cfb672ed31c595c6f8531d17b5704315b06cba07aabbc35d8dbccc3fe0ed4f1c290e33d9e599e2f75af9d51afdb1fe languageName: node linkType: hard -"@aws-sdk/client-sts@npm:3.296.0, @aws-sdk/client-sts@npm:^3.208.0": - version: 3.296.0 - resolution: "@aws-sdk/client-sts@npm:3.296.0" +"@aws-sdk/client-sts@npm:3.298.0, @aws-sdk/client-sts@npm:^3.208.0": + version: 3.298.0 + resolution: "@aws-sdk/client-sts@npm:3.298.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 "@aws-sdk/config-resolver": 3.296.0 - "@aws-sdk/credential-provider-node": 3.296.0 + "@aws-sdk/credential-provider-node": 3.298.0 "@aws-sdk/fetch-http-handler": 3.296.0 "@aws-sdk/hash-node": 3.296.0 "@aws-sdk/invalid-dependency": 3.296.0 @@ -789,7 +789,7 @@ __metadata: "@aws-sdk/util-utf8": 3.295.0 fast-xml-parser: 4.1.2 tslib: ^2.5.0 - checksum: c7a7837601d3bf2cf80e00a498e4f3412d88cccb605a251e65b949e8495874cb7738f0979dc51857f7606e76a568c9c15ecc77800191f54fef181c5cb2bca97b + checksum: a46024185263e548101746cc6c1841d904b02a5bc44e580384bd616ab5325ecb2d27bd0231bce7fab9796e10ce60387c98853e5915b4718f9b2715482c5eca46 languageName: node linkType: hard @@ -806,15 +806,15 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/credential-provider-cognito-identity@npm:3.296.0": - version: 3.296.0 - resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.296.0" +"@aws-sdk/credential-provider-cognito-identity@npm:3.298.0": + version: 3.298.0 + resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.298.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.296.0 + "@aws-sdk/client-cognito-identity": 3.298.0 "@aws-sdk/property-provider": 3.296.0 "@aws-sdk/types": 3.296.0 tslib: ^2.5.0 - checksum: ef2046e91aa3235e894ac762da7a4d6146fb383f094e504e6a0c6a6950f827c3c81e6826dd622483db5b13df234aa8e73cc4bad089753ef9ac3a1ffb390f75fa + checksum: d6bf9bf5c3d5ef916b931a4a69ae3e54cd90ff86da508a22e24b1b401f771461be73ccacf3aaed844b27019355f3c0c2a64af5815140fdcd7438c7190dc7a207 languageName: node linkType: hard @@ -842,38 +842,38 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/credential-provider-ini@npm:3.296.0": - version: 3.296.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.296.0" +"@aws-sdk/credential-provider-ini@npm:3.298.0": + version: 3.298.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.298.0" dependencies: "@aws-sdk/credential-provider-env": 3.296.0 "@aws-sdk/credential-provider-imds": 3.296.0 "@aws-sdk/credential-provider-process": 3.296.0 - "@aws-sdk/credential-provider-sso": 3.296.0 + "@aws-sdk/credential-provider-sso": 3.298.0 "@aws-sdk/credential-provider-web-identity": 3.296.0 "@aws-sdk/property-provider": 3.296.0 "@aws-sdk/shared-ini-file-loader": 3.296.0 "@aws-sdk/types": 3.296.0 tslib: ^2.5.0 - checksum: 8c3a518ab5eb1b2153f722548e50c11af1cc2d8d2b3eafe5ddb1022bb677eb6fb6cc41bd60a508d45c322c6a653511d9445ca335ea103890f7b534ed0fdfab78 + checksum: b729aa62ea23377185c031d5738f4d97e263b2e24eae8643a12ce493ba63729a19c4590f861ead268c6264f5b1bb06dd4cff8e52b4cc0b906c3dae254ab1d11c languageName: node linkType: hard -"@aws-sdk/credential-provider-node@npm:3.296.0, @aws-sdk/credential-provider-node@npm:^3.208.0": - version: 3.296.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.296.0" +"@aws-sdk/credential-provider-node@npm:3.298.0, @aws-sdk/credential-provider-node@npm:^3.208.0": + version: 3.298.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.298.0" dependencies: "@aws-sdk/credential-provider-env": 3.296.0 "@aws-sdk/credential-provider-imds": 3.296.0 - "@aws-sdk/credential-provider-ini": 3.296.0 + "@aws-sdk/credential-provider-ini": 3.298.0 "@aws-sdk/credential-provider-process": 3.296.0 - "@aws-sdk/credential-provider-sso": 3.296.0 + "@aws-sdk/credential-provider-sso": 3.298.0 "@aws-sdk/credential-provider-web-identity": 3.296.0 "@aws-sdk/property-provider": 3.296.0 "@aws-sdk/shared-ini-file-loader": 3.296.0 "@aws-sdk/types": 3.296.0 tslib: ^2.5.0 - checksum: ccc0616dc331764a7378bad898982c2dfb56c8c2d52d4eed003a5265105dfab2598bf40761ef231df28bf9bce17671e86af88415784ae779979ab9c55d75aeb8 + checksum: 7ac85fff40f4c266f5890ae02998f166c10cda43c6ce50b59c475e776be3356cadca8bf00f0f687c62a38ce6030597e850f98068c862a1103fee62198c206f3a languageName: node linkType: hard @@ -889,17 +889,17 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/credential-provider-sso@npm:3.296.0": - version: 3.296.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.296.0" +"@aws-sdk/credential-provider-sso@npm:3.298.0": + version: 3.298.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.298.0" dependencies: - "@aws-sdk/client-sso": 3.296.0 + "@aws-sdk/client-sso": 3.298.0 "@aws-sdk/property-provider": 3.296.0 "@aws-sdk/shared-ini-file-loader": 3.296.0 - "@aws-sdk/token-providers": 3.296.0 + "@aws-sdk/token-providers": 3.298.0 "@aws-sdk/types": 3.296.0 tslib: ^2.5.0 - checksum: c4916c1d923831240b65bd51c1db3e0f80ce65f2caedf5e45c4799cf0db71ef79b27cd0394a1bd2c94a90bd65ae08b6cbc36416f3c91f7e6c41339c5ef00f919 + checksum: cabb308ec19016f7ab416e2f77b9ec4293c1b8a7e129c60133f19532cd9fedb2dbef6471e48119b8e084539cd095b08ad810680d82d2059288ac43d69cea2163 languageName: node linkType: hard @@ -915,25 +915,25 @@ __metadata: linkType: hard "@aws-sdk/credential-providers@npm:^3.208.0": - version: 3.296.0 - resolution: "@aws-sdk/credential-providers@npm:3.296.0" + version: 3.298.0 + resolution: "@aws-sdk/credential-providers@npm:3.298.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.296.0 - "@aws-sdk/client-sso": 3.296.0 - "@aws-sdk/client-sts": 3.296.0 - "@aws-sdk/credential-provider-cognito-identity": 3.296.0 + "@aws-sdk/client-cognito-identity": 3.298.0 + "@aws-sdk/client-sso": 3.298.0 + "@aws-sdk/client-sts": 3.298.0 + "@aws-sdk/credential-provider-cognito-identity": 3.298.0 "@aws-sdk/credential-provider-env": 3.296.0 "@aws-sdk/credential-provider-imds": 3.296.0 - "@aws-sdk/credential-provider-ini": 3.296.0 - "@aws-sdk/credential-provider-node": 3.296.0 + "@aws-sdk/credential-provider-ini": 3.298.0 + "@aws-sdk/credential-provider-node": 3.298.0 "@aws-sdk/credential-provider-process": 3.296.0 - "@aws-sdk/credential-provider-sso": 3.296.0 + "@aws-sdk/credential-provider-sso": 3.298.0 "@aws-sdk/credential-provider-web-identity": 3.296.0 "@aws-sdk/property-provider": 3.296.0 "@aws-sdk/shared-ini-file-loader": 3.296.0 "@aws-sdk/types": 3.296.0 tslib: ^2.5.0 - checksum: 5daa3d2e69c346f37f9c752c8e25953678d2571c911bb3da4d6b71b7f2ab5af3066890b3116b176a5d1aa9d7163e80c32db70d61bcd901dc0091711fa313a763 + checksum: 133c41843a4d5913ab7ec8a833a7868cb243ed56a574e3ab8f557bcc0366933ce09f06d48e17e461eeaf0eb7f86991e527f76b9b22fbb082d5c9af804fe4fc5d languageName: node linkType: hard @@ -1060,8 +1060,8 @@ __metadata: linkType: hard "@aws-sdk/lib-storage@npm:^3.208.0": - version: 3.296.0 - resolution: "@aws-sdk/lib-storage@npm:3.296.0" + version: 3.298.0 + resolution: "@aws-sdk/lib-storage@npm:3.298.0" dependencies: "@aws-sdk/middleware-endpoint": 3.296.0 "@aws-sdk/smithy-client": 3.296.0 @@ -1072,7 +1072,7 @@ __metadata: peerDependencies: "@aws-sdk/abort-controller": ^3.0.0 "@aws-sdk/client-s3": ^3.0.0 - checksum: 2d609ea842dbef07ed6b4ed5d5593d562332dd81141398e9e41e2f8282e37e8293021fa416c28d3c2430b5ffc16e301d09073fd2d20e85983f2ec2352b19eba2 + checksum: 5148a92123a4bbf596ead6901cdbfb4985cc59a50437593dfc483ddf29c66a9d9fbf3040b4b6675a2b92517a01bb9fbf0cffc31c3391ad212787cc77d5867d51 languageName: node linkType: hard @@ -1430,16 +1430,16 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/token-providers@npm:3.296.0": - version: 3.296.0 - resolution: "@aws-sdk/token-providers@npm:3.296.0" +"@aws-sdk/token-providers@npm:3.298.0": + version: 3.298.0 + resolution: "@aws-sdk/token-providers@npm:3.298.0" dependencies: - "@aws-sdk/client-sso-oidc": 3.296.0 + "@aws-sdk/client-sso-oidc": 3.298.0 "@aws-sdk/property-provider": 3.296.0 "@aws-sdk/shared-ini-file-loader": 3.296.0 "@aws-sdk/types": 3.296.0 tslib: ^2.5.0 - checksum: 02d5294fcec153977a7edff3b1843a9c935b11b59adbde26dc84cc5e793d2850b9d4736b29425f384a5c01ca0d747f5b3686d42e1370dae8ed9bc298eda1d888 + checksum: aed172e630a4d5de12f77564782486989cf12fe90b837bf0b82347cf1ed6c73c086aacad284d753a9e47676d4bdf251232b845d57f2f9943e5c815bb9321b465 languageName: node linkType: hard @@ -7608,6 +7608,29 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-scaffolder-backend-module-confluence-to-markdown@workspace:plugins/scaffolder-backend-module-confluence-to-markdown": + version: 0.0.0-use.local + resolution: "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown@workspace:plugins/scaffolder-backend-module-confluence-to-markdown" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/integration": "workspace:^" + "@backstage/plugin-scaffolder-backend": "workspace:^" + "@backstage/plugin-scaffolder-node": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@backstage/types": "workspace:^" + fs-extra: 10.1.0 + git-url-parse: ^13.1.0 + mock-fs: ^5.2.0 + msw: ^1.0.0 + node-fetch: ^2.6.7 + node-html-markdown: ^1.3.0 + yaml: ^2.0.0 + languageName: unknown + linkType: soft + "@backstage/plugin-scaffolder-backend-module-cookiecutter@workspace:plugins/scaffolder-backend-module-cookiecutter": version: 0.0.0-use.local resolution: "@backstage/plugin-scaffolder-backend-module-cookiecutter@workspace:plugins/scaffolder-backend-module-cookiecutter" @@ -20245,11 +20268,11 @@ __metadata: linkType: hard "cronstrue@npm:^2.2.0": - version: 2.23.0 - resolution: "cronstrue@npm:2.23.0" + version: 2.24.0 + resolution: "cronstrue@npm:2.24.0" bin: cronstrue: bin/cli.js - checksum: 050140ac6452468d6f9032f18038f41b16968502d411a996efd5d42243cfd882271ff20ed4a3cdb25c45bf8f88beeb0579e45e974a11987401e9d383af2641a8 + checksum: 2c7d4cfe43bec7b57b1824772641271bb5f5ed0209336ea53796f97b17966dd95cf15ae890f7c2494f639ba489d225bfb882950cda0ec7e5453fa7bf236842dd languageName: node linkType: hard @@ -20407,6 +20430,19 @@ __metadata: languageName: node linkType: hard +"css-select@npm:^5.1.0": + version: 5.1.0 + resolution: "css-select@npm:5.1.0" + dependencies: + boolbase: ^1.0.0 + css-what: ^6.1.0 + domhandler: ^5.0.2 + domutils: ^3.0.1 + nth-check: ^2.0.1 + checksum: 2772c049b188d3b8a8159907192e926e11824aea525b8282981f72ba3f349cf9ecd523fdf7734875ee2cb772246c22117fc062da105b6d59afe8dcd5c99c9bda + languageName: node + linkType: hard + "css-to-react-native@npm:^3.0.0": version: 3.0.0 resolution: "css-to-react-native@npm:3.0.0" @@ -20461,6 +20497,13 @@ __metadata: languageName: node linkType: hard +"css-what@npm:^6.1.0": + version: 6.1.0 + resolution: "css-what@npm:6.1.0" + checksum: b975e547e1e90b79625918f84e67db5d33d896e6de846c9b584094e529f0c63e2ab85ee33b9daffd05bff3a146a1916bec664e18bb76dd5f66cbff9fc13b2bbe + languageName: node + linkType: hard + "css.escape@npm:1.5.1, css.escape@npm:^1.5.1": version: 1.5.1 resolution: "css.escape@npm:1.5.1" @@ -24459,7 +24502,7 @@ __metadata: languageName: node linkType: hard -"git-url-parse@npm:^13.0.0": +"git-url-parse@npm:^13.0.0, git-url-parse@npm:^13.1.0": version: 13.1.0 resolution: "git-url-parse@npm:13.1.0" dependencies: @@ -25143,7 +25186,7 @@ __metadata: languageName: node linkType: hard -"he@npm:^1.2.0": +"he@npm:1.2.0, he@npm:^1.2.0": version: 1.2.0 resolution: "he@npm:1.2.0" bin: @@ -25666,9 +25709,9 @@ __metadata: linkType: hard "immer@npm:^9.0.1, immer@npm:^9.0.7": - version: 9.0.19 - resolution: "immer@npm:9.0.19" - checksum: f02ee53989989c287cd548a3d817fccf0bfe56db919755ee94a72ea3ae78a00363fba93ee6c010fe54a664380c29c53d44ed4091c6a86cae60957ad2cfabc010 + version: 9.0.21 + resolution: "immer@npm:9.0.21" + checksum: 70e3c274165995352f6936695f0ef4723c52c92c92dd0e9afdfe008175af39fa28e76aafb3a2ca9d57d1fb8f796efc4dd1e1cc36f18d33fa5b74f3dfb0375432 languageName: node linkType: hard @@ -29354,11 +29397,11 @@ __metadata: linkType: hard "marked@npm:^4.0.10, marked@npm:^4.0.14": - version: 4.2.12 - resolution: "marked@npm:4.2.12" + version: 4.3.0 + resolution: "marked@npm:4.3.0" bin: marked: bin/marked.js - checksum: bd551cd61028ee639d4ca2ccdfcc5a6ba4227c1b143c4538f3cde27f569dcb57df8e6313560394645b418b84a7336c07ab1e438b89b6324c29d7d8cdd3102d63 + checksum: 0db6817893952c3ec710eb9ceafb8468bf5ae38cb0f92b7b083baa13d70b19774674be04db5b817681fa7c5c6a088f61300815e4dd75a59696f4716ad69f6260 languageName: node linkType: hard @@ -30905,6 +30948,25 @@ __metadata: languageName: node linkType: hard +"node-html-markdown@npm:^1.3.0": + version: 1.3.0 + resolution: "node-html-markdown@npm:1.3.0" + dependencies: + node-html-parser: ^6.1.1 + checksum: 303affd75bf11e92f004047e283aa05da5c8ae8696c1f28f811e9f61762c55b4480e0e5986a9a4b399791485b835380af8ab0554ebf867d6cf9be362abedcf5d + languageName: node + linkType: hard + +"node-html-parser@npm:^6.1.1": + version: 6.1.5 + resolution: "node-html-parser@npm:6.1.5" + dependencies: + css-select: ^5.1.0 + he: 1.2.0 + checksum: b54257b31954be17d473c86a2fede53d5e238708f3d88577c98881c28ab66013fd37044903f6f3cd662b56d882681fb511bca75a80a80b62f7111b32b39eebae + languageName: node + linkType: hard + "node-int64@npm:^0.4.0": version: 0.4.0 resolution: "node-int64@npm:0.4.0" @@ -31205,12 +31267,12 @@ __metadata: languageName: node linkType: hard -"nth-check@npm:^2.0.0": - version: 2.0.1 - resolution: "nth-check@npm:2.0.1" +"nth-check@npm:^2.0.0, nth-check@npm:^2.0.1": + version: 2.1.1 + resolution: "nth-check@npm:2.1.1" dependencies: boolbase: ^1.0.0 - checksum: 5386d035c48438ff304fe687704d93886397349d1bed136de97aeae464caba10e8ffac55a04b215b86b3bc8897f33e0a5aa1045a9d8b2f251ae61b2a3ad3e450 + checksum: 5afc3dafcd1573b08877ca8e6148c52abd565f1d06b1eb08caf982e3fa289a82f2cae697ffb55b5021e146d60443f1590a5d6b944844e944714a5b549675bcd3 languageName: node linkType: hard