From 6ec07d6aa16d854f0216caa80314e1f679a9d820 Mon Sep 17 00:00:00 2001 From: Waldir Date: Mon, 18 Mar 2024 14:39:55 -0500 Subject: [PATCH 001/251] BEP 0006 Describe personas and separate portal and framework docs Signed-off-by: Waldir --- .../README.md | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 beps/0006-docs-personas-framework-portal/README.md diff --git a/beps/0006-docs-personas-framework-portal/README.md b/beps/0006-docs-personas-framework-portal/README.md new file mode 100644 index 0000000000..96f9a5944a --- /dev/null +++ b/beps/0006-docs-personas-framework-portal/README.md @@ -0,0 +1,240 @@ +--- +title: Restructuring Backstage Documentation for Improved Navigation and Clarity +status: provisional +authors: + - '@waldirmontoya25' +owners: +project-areas: + - Documentation +creation-date: 2024-03-18 +--- + +# BEP: Enhancing Backstage Documentation: Personas, Framework, and Developer Portal + +[**Discussion Issue**](https://github.com/backstage/backstage/issues/23689) + +- [BEP: Enhancing Backstage Documentation: Personas, Framework, and Developer Portal](#bep-enhancing-backstage-documentation-personas-framework-and-developer-portal) + - [Summary](#summary) + - [Motivation](#motivation) + - [Goals](#goals) + - [Non-Goals](#non-goals) + - [Proposal](#proposal) + - [Design Details](#design-details) + - [Release Plan](#release-plan) + - [Dependencies](#dependencies) + - [Example Table of Contents](#example-table-of-contents) + +## Summary + +This BEP proposes restructuring the Backstage documentation to emphasize the dual nature of Backstage as both a framework for building developer portals and a fully functional developer portal out of the box, as demonstrated by the demo site. The documentation will be divided into two main sections: One focusing on the developer portal that users get with the core plugins, and another on the framework that allows integrators and builders to create their own developer portal. The goal is to improve clarity, navigation, and adoption of Backstage by positioning it as both a ready-to-use developer portal and a framework for building custom developer portals. + +## Motivation + +The current Backstage documentation has been reported to be difficult to navigate, making it challenging for different personas across the DevEx ecosystem to extract value. The CNCF [**assessment**](https://github.com/cncf/techdocs/tree/main/assessments/0008-backstage/) and the resulting [**issues**](https://github.com/backstage/backstage/issues/21893) highlight the need for improvement in the documentation structure. + +### Goals + +- Divide the documentation into two section: Framework and Developer Portal +- Define the personas Backstage is targeting +- Structure the documentation to cater the different personas +- Move the existing content to the appropriate section + +### Non-Goals + +- Rewrite the entire documentation from scratch +- Write additional content beyond the scope of the existing documentation + +## Proposal + +The proposed restructuring of the Backstage documentation revolves around two core ideas: + +1. Positioning Backstage as both a framework to build developer portals and a developer portal itself, and splitting the documentation into two main sections: + + - Developer Portal: Focusing on the features, configuration, and usage of the developer portal that users get out of the box with the core plugins. + - Framework: Covering the aspects of Backstage as a framework, including guides for integrators and builders who want to create their own developer portal using Backstage. + +2. Defining the personas participating in Backstage adoption journeys to improve documentation navigation. The identified personas are: + + - **User**: A person who uses Backstage to find information, use plugins, and consume the developer portal. + - **Administrator/Operator**: A person who configures, secures, and deploys the developer portal, manages plugins, and oversees the general administration of the developer portal. + - **Integrator/Builder**: A person who builds plugins, customizes the code and design, and creates custom-built developer portals based on the Backstage framework. This includes developers and designers and anyone adding new functionality to their own Backstage instance. + - **Product Manager/Business stakeholders**: A person who defines the strategy for adopting Backstage, identifies use cases, communicates the value proposition for adopting Backstage and connects the developer portal to the business strategy. + - **Contributor**: A person who contributes to the Backstage upstream ecosystem. + +The adoption strategy would be as follows: + +- Create a dedicated page describing the personas Backstage is targeting and the documentation sections that cater to each persona. +- Restructure the documentation into the two new sections (Framework and Developer Portal) and redistribute the existing content accordingly. +- Publish a blog post announcing the changes, highlighting Backstage's positioning as both a framework and a developer portal, and explaining the benefits of the restructured documentation. + +The benefits of restructuring the documentation according to these ideas include: + +1. Easier navigation and discoverability of information for different personas and use cases. +2. Clear separation of runtime and development documentation. +3. Simplified process for contributors to determine the appropriate location for new documentation. +4. Streamlined Backstage adoption process for new adopters. + +## Design Details + +- The Docs section of the microsite will be divided into two top-level sections: Framework and Developer Portal. +- The structure of the Table of Contents will align with the outline proposed in https://github.com/backstage/backstage/issues/21946. + +## Release Plan + +- Release the BEP by 03/24/2024. +- Discuss the changes with the community and gather feedback by 04/24/2024. +- Implement the changes by 04/30/2024. + +## Dependencies + +None + +## Example Table of Contents + +- Overview + - What is Backstage? + - Roadmap + - Vision + - Release and Versioning Policy + - Backstage Threat Model + - Logo assets + - Support and community +- Framework + - Architecture Overview + - Getting Started + - Integrator/Builder Guides + - Local Development + - CLI + - Linking in local packages + - Debugging Backstage + - Backstage core framework + - Systems + - Frontend + - Old + - New + - Backend + - Old + - New + - API Reference + - Tutorials + - Building plugins + - Intro to plugins + - Existing plugins + - Creating a new plugin + - Plugin development + - Structuring a plugin + - Integrating with other systems + - Integrating with the Catalog + - Integrating Search + - Composability system + - Internationalization + - Plugin analytics + - Feature flags + - OpenAPI + - Backends and APIs + - Testing + - Publishing + - Home Page + - Customizing the home page + - Software Catalog + - Extending the model + - External integrations + - Catalog Customization + - API + - Software Templates + - Writing custom actions + - Writing tests for actions + - Writing custom field extensions + - Writing custom step layouts + - Authorizing parameters, steps and actions + - Migrating to react-jsonschema-form@v5 + - Migrating to v1beta3 templates + - Search + - Overview + - Getting Started with search + - Search concepts + - Search architecture + - Search Engines + - How to Guides + - TechDocs + - Customizing TechDocs + - TechDocs add-ons + - Kubernetes + - Customizing the kubernetes plugin + - Authentication + - Proxy + - Permissions + - Overview + - Concepts + - Getting Started + - Writing a permission policy + - Frontend integration + - Defining custom permission rules + - Using permissions in plugins + - Designing for Backstage + - ADRs + - Accessibility + - References + - Contributor Guides + - Contributing to Backstage + - Reference +- Developer Portal + - Architecture Overview + - Getting Started + - Administrator Guides + - Developer Portal + - Installing and Configuring + - Database + - Authentication + - Installing plugins + - Customize the design + - Securing + - Deploying in Production + - Integrating with other systems + - Managing + - Monitoring + - Troubleshooting + - Upgrading + - Keeping backstage up to date + - Customizing + - Home Page + - Installing and Configuring + - Software Catalog + - Overview + - The life of an Entity + - Catalog Configuration + - System Model + - YAML file format + - Entity Reference + - Well Known annotations + - Well known relations + - Well known statuses + - Creating the catalog graph + - Software Templates + - Overview + - Configuring + - Adding a new template + - Writing a template + - Built in actions + - TechDocs + - Overview + - Getting Started + - Architecture + - Installing and configuring + - Using Cloud Storage for TechDocs generated files + - Configuring CI/CD to generate and publish TechDocs sites + - TechDocs CLI + - Troubleshooting + - Kubernetes + - Installing and Configuring + - Authentication + - Troubleshooting + - Search + - Product Manager Guides + - Strategies for adopting + - Use cases + - User Guides + - Logging in + - Registering a component + - Creating a new component + - Reference From 2507e3d0db9b6c05c6f9bfae94b3f787d9825338 Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Tue, 19 Mar 2024 23:37:32 +0100 Subject: [PATCH 002/251] bep: add user notification settings plan Signed-off-by: Heikki Hellgren --- beps/0001-notifications-system/README.md | 31 +++++++++++++++++- .../UserNotificationSettings.png | Bin 0 -> 29081 bytes 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 beps/0001-notifications-system/UserNotificationSettings.png diff --git a/beps/0001-notifications-system/README.md b/beps/0001-notifications-system/README.md index 66e23542dc..bd3a097dea 100644 --- a/beps/0001-notifications-system/README.md +++ b/beps/0001-notifications-system/README.md @@ -380,10 +380,39 @@ interface SignalApi { - Render dynamic values with various different React elements such as the `EntityRefLink` for entity references (for example `{{ user:default/john.doe }}`) in the notification payload - Handle `link` values that use route references. For example instead hard-coding link to `/catalog/default/component/artist-web` it should be possible to use `catalogPlugin.catalogEntity` route reference as a link of the notification. This should also allow using parameters to be passed to the route reference. Links to external systems are still supported. - Add support for `analyticsApi` to notification actions like marking notifications done, saved or opening links in the notifications -- Add support for user settings to control how notifications are shown to the user and which notifications user wants to receive. This should also include support for different `NotificationProcessor`s that can send notification to external systems - Add a sound to be played when notification is received - Add i18n internationalization support for the notification payload +### User specific notification settings + +To allow the users more fine-grained control of notifications, user must have an option to unsubscribe from specific origins, topics and channels. + +By default, all notifications are enabled for the user in all notification channels. Frontend plugin provides an user interface to change these settings for example as described in the following image: + +![user notification settings UI example](./UserNotificationSettings.png) + +Each notification processor with `send` functionality will be listed as a separate channel among the Backstage internal notification channel `Web`. For this, each notification processor must implement a function to get a human readable name to be rendered in the frontend. + +User must be able to save these settings to the notification database, and they are be checked each time a new notification is sent to the user. + +Disabling a notification from specific origin or topic in the `Web` channel will not remove the old notifications being visible in the frontend from that source but instead, it only prevents new notifications being sent. + +For performance reasons the notification settings will ignore all broadcast notifications and those will be sent to all users despite their origin or channel. + +```ts +export type NotificationSetting = { + origin: string; + topic?: string; + channels: Record; +}; + +export type UserNotificationSettings = { + settings: NotificationSetting[]; +}; +``` + +The backend plugin returns user notification settings for all known origins, topics and channels it knows of. As new origins, topics and channels will get added, also the notification settings list will get longer. This allows an easy way to add new notification origins, topics and channels without need to change the notification plugin. If the user is missing notification setting from specific origin/topic/channel, the backend considers it as enabled. + ## Release Plan The notification and signal plugins are released as two new plugins in the Backstage ecosystem. They will both start out in an experimental state. diff --git a/beps/0001-notifications-system/UserNotificationSettings.png b/beps/0001-notifications-system/UserNotificationSettings.png new file mode 100644 index 0000000000000000000000000000000000000000..de9a5881b43a47c0c28778eae6c609ec20d659c0 GIT binary patch literal 29081 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 p`T31&oQq26@WiBJWWGD@t=-~1<4^wx-nF Date: Tue, 19 Mar 2024 09:30:03 +0100 Subject: [PATCH 003/251] possible updates Signed-off-by: aramissennyeydd --- .../README.md | 196 +++++++++++------- 1 file changed, 122 insertions(+), 74 deletions(-) diff --git a/beps/0006-docs-personas-framework-portal/README.md b/beps/0006-docs-personas-framework-portal/README.md index 96f9a5944a..e79d6b11a7 100644 --- a/beps/0006-docs-personas-framework-portal/README.md +++ b/beps/0006-docs-personas-framework-portal/README.md @@ -79,6 +79,41 @@ The benefits of restructuring the documentation according to these ideas include - The Docs section of the microsite will be divided into two top-level sections: Framework and Developer Portal. - The structure of the Table of Contents will align with the outline proposed in https://github.com/backstage/backstage/issues/21946. +### User Persona + +Documentation written for the user persona should be high level, assuming little to no technical knowledge. Concepts like the difference between Backstage Framework and Developer Portal should be explained at a high level to give Users a working knowledge, but should not be flushed out completely until we identify the user as a distinct user persona. Users may also be Administrators, Integrators or Product Managers, but documentation for Users should make no assumptions on that. + +Example explanation of Backstage Framework vs Developer Portal: +"Backstage has two meanings, the Backstage Framework and the Backstage Developer Portal. Backstage Framework provides the tools you need to build your own developer portal and Backstage Developer Portal is your custom developer portal built using the Backstage Framework." + +### Administrator Persona + +Documentation written for this persona should be DevOps technical, assuming a strong DevOps background with the exception of a Getting Started section. While we should assume an overall technical knowledge, where possible we should link out to existing strong guides for the technologies we use, ex: PostgreSQL, Docker, Kubernetes, etc. The goal with administrator documentation is to give administrators a strong understanding of how to deploy and manage a Backstage Developer Portal, best practices, and possible tripping points. + +Example explanation of Backstage Framework vs Developer Portal: +"Backstage has two meanings, the Backstage Framework and the Backstage Developer Portal. As an administrator, you will be interacting with Backstage Developer Portal primarily -- this is the running Backstage instance that you're managing. It is also useful to have a high level understanding of the Backstage Framework for mitigating issues or better understanding how to scale your Backstage Developer Portal." + +### Integrator Persona + +Documentation written for this persona should be software technical, assuming a strong software background with the exception of a Getting Started section. While we can assume an overall technical knowledge, where possible we should link out to useful guides for the technologies we use, ex: Node.js, express.js, React, etc. The goal with documentation written for integrators is to give them a strong understanding of where their work fits into their company's Backstage Developer Portal, orient them to get support from the open source community, and prepare them for continuing to deliver value for their Backstage Developer Portal. + +Example explanation of Backstage Framework vs Developer Portal: +"Backstage has two meanings, the Backstage Framework and the Backstage Developer Portal. As an integrator, most of your time will be spent working to deliver value on top of the Backstage Framework for your company's Backstage Developer Portal. This means creating plugins, theming your Backstage Developer Portal or adding integrations to internal data stores. You should have a strong understanding of the Backstage Framework and have a strong understaing of code that sits in your Backstage Developer Portal." + +### Business Stakeholder + +Documentation written for this persona should be strategic, assuming a strong background in business development and strategy. The goal for business documentation is to give a strong understanding of what Backstage Developer Portal can do for their company, how to deliver value quickly and continuously and guides for pitching or driving Backstage adoption. + +Example explanation of Backstage Framework vs Developer Portal: +"Backstage has two meanings, the Backstage Framework and the Backstage Developer Portal. As a business stakeholder, your time should be spent with your company's Backstage Developer Portal solely. This means understanding the value proposition of an IDP, what improving the developer experience at your company means and what a successful Backstage Developer Portal looks like. The Backstage Framework is the technical underpinning and will generally be invisible to you." + +## Contributor + +Documentation written for this role should be technical, but should make no assumptions on technical strength. The goal with this documentation is to onboard new contributors to the technical stack and layout of the project, setting expectations for how to write code, documentation or generally contribute to the library. + +Example explanation of Backstage Framework vs Developer Portal: +"Backstage has two meanings, the Backstage Framework and the Backstage Developer Portal. You will work with both the Backstage Framework and Backstage Developer Portal. Backstage Framework is the technical framework for Backstage Developer Portal. The Backstage Developer Portal is the end user for your plugins, documentation or other contributions. As a contributor, you will be working to influence either the Backstage Framework or plugins and Backstage Developer Portals across the world may use your contributions." + ## Release Plan - Release the BEP by 03/24/2024. @@ -92,6 +127,7 @@ None ## Example Table of Contents - Overview + - "The overview should introduce users to the concept of Backstage, what an IDP is, how to deliver value, why you should care about DevEx, etc." - What is Backstage? - Roadmap - Vision @@ -101,13 +137,16 @@ None - Support and community - Framework - Architecture Overview + - "The arch overview should explain how the framework is structured, where plugins and instances fit in and how to understand the current design of Backstage." - Getting Started - Integrator/Builder Guides - Local Development + - "Prepare users for how to develop locally, debug problems, run tests, etc." - CLI - Linking in local packages - Debugging Backstage - Backstage core framework + - "Internal documentation." - Systems - Frontend - Old @@ -116,8 +155,9 @@ None - Old - New - API Reference - - Tutorials + - "Internal documentation" - Building plugins + - "How to build a plugin, how to integrate it with other plugins, how to deploy and monitor it, and how to iterate on plugin development." - Intro to plugins - Existing plugins - Creating a new plugin @@ -134,48 +174,51 @@ None - Backends and APIs - Testing - Publishing - - Home Page - - Customizing the home page - - Software Catalog - - Extending the model - - External integrations - - Catalog Customization - - API - - Software Templates - - Writing custom actions - - Writing tests for actions - - Writing custom field extensions - - Writing custom step layouts - - Authorizing parameters, steps and actions - - Migrating to react-jsonschema-form@v5 - - Migrating to v1beta3 templates - - Search - - Overview - - Getting Started with search - - Search concepts - - Search architecture - - Search Engines - - How to Guides - - TechDocs - - Customizing TechDocs - - TechDocs add-ons - - Kubernetes - - Customizing the kubernetes plugin - - Authentication - - Proxy - - Permissions - - Overview - - Concepts - - Getting Started - - Writing a permission policy - - Frontend integration - - Defining custom permission rules - - Using permissions in plugins - - Designing for Backstage - - ADRs - - Accessibility - - References + - Core Plugins + - "How to leverage the existing plugins for your new plugin or customization options." + - Home Page + - Customizing the home page + - Software Catalog + - Extending the model + - External integrations + - Catalog Customization + - API + - Software Templates + - Writing custom actions + - Writing tests for actions + - Writing custom field extensions + - Writing custom step layouts + - Authorizing parameters, steps and actions + - Migrating to react-jsonschema-form@v5 + - Migrating to v1beta3 templates + - Search + - Overview + - Getting Started with search + - Search concepts + - Search architecture + - Search Engines + - How to Guides + - TechDocs + - Customizing TechDocs + - TechDocs add-ons + - Kubernetes + - Customizing the kubernetes plugin + - Authentication + - Proxy + - Permissions + - Overview + - Concepts + - Getting Started + - Writing a permission policy + - Frontend integration + - Defining custom permission rules + - Using permissions in plugins + - Designing for Backstage + - ADRs + - Accessibility + - References - Contributor Guides + - "How to get started contributing to OSS." - Contributing to Backstage - Reference - Developer Portal @@ -183,6 +226,7 @@ None - Getting Started - Administrator Guides - Developer Portal + - "How do I deploy, monitor, configure and verify my Backstage Developer Portal?" - Installing and Configuring - Database - Authentication @@ -197,43 +241,47 @@ None - Upgrading - Keeping backstage up to date - Customizing - - Home Page - - Installing and Configuring - - Software Catalog - - Overview - - The life of an Entity - - Catalog Configuration - - System Model - - YAML file format - - Entity Reference - - Well Known annotations - - Well known relations - - Well known statuses - - Creating the catalog graph - - Software Templates - - Overview - - Configuring - - Adding a new template - - Writing a template - - Built in actions - - TechDocs - - Overview - - Getting Started - - Architecture - - Installing and configuring - - Using Cloud Storage for TechDocs generated files - - Configuring CI/CD to generate and publish TechDocs sites - - TechDocs CLI + - Core Plugins + - "How do I install and configure Backstage Developer Portal with plugins." + - Home Page + - Installing and Configuring + - Software Catalog + - Overview + - The life of an Entity + - Catalog Configuration + - System Model + - YAML file format + - Entity Reference + - Well Known annotations + - Well known relations + - Well known statuses + - Creating the catalog graph + - Software Templates + - Overview + - Configuring + - Adding a new template + - Writing a template + - Built in actions + - TechDocs + - Overview + - Getting Started + - Architecture + - Installing and configuring + - Using Cloud Storage for TechDocs generated files + - Configuring CI/CD to generate and publish TechDocs sites + - TechDocs CLI + - Troubleshooting + - Kubernetes + - Installing and Configuring + - Authentication - Troubleshooting - - Kubernetes - - Installing and Configuring - - Authentication - - Troubleshooting - - Search + - Search - Product Manager Guides + - "How do I present Backstage to leadership, what are the benefits, why should I care, etc." - Strategies for adopting - Use cases - User Guides + - "How do I use the default OSS Backstage" - Logging in - Registering a component - Creating a new component From 596dea12617cdec16f7c1b4467a012607823bf58 Mon Sep 17 00:00:00 2001 From: Waldir Montoya Date: Wed, 20 Mar 2024 12:21:20 -0500 Subject: [PATCH 004/251] change BEP number and enhanced Persona description Signed-off-by: Waldir Montoya --- .../README.md | 101 ++++++++++++++---- 1 file changed, 80 insertions(+), 21 deletions(-) rename beps/{0006-docs-personas-framework-portal => 0007-docs-personas-framework-portal}/README.md (67%) diff --git a/beps/0006-docs-personas-framework-portal/README.md b/beps/0007-docs-personas-framework-portal/README.md similarity index 67% rename from beps/0006-docs-personas-framework-portal/README.md rename to beps/0007-docs-personas-framework-portal/README.md index e79d6b11a7..b18e4896ec 100644 --- a/beps/0006-docs-personas-framework-portal/README.md +++ b/beps/0007-docs-personas-framework-portal/README.md @@ -1,8 +1,9 @@ --- -title: Restructuring Backstage Documentation for Improved Navigation and Clarity +title: Enhancing Backstage Documentation, Personas, Framework, and Developer Portal status: provisional authors: - '@waldirmontoya25' + - '@aramissennyeyd' owners: project-areas: - Documentation @@ -20,6 +21,17 @@ creation-date: 2024-03-18 - [Non-Goals](#non-goals) - [Proposal](#proposal) - [Design Details](#design-details) + - [Personas](#personas) + - [User](#user) + - [Documentation Style](#documentation-style) + - [Administrator](#administrator) + - [Documentation Style](#documentation-style-1) + - [Integrator](#integrator) + - [Documentation Style](#documentation-style-2) + - [Contributor](#contributor) + - [Documentation Style](#documentation-style-3) + - [Business Stakeholder](#business-stakeholder) + - [Documentation Style](#documentation-style-4) - [Release Plan](#release-plan) - [Dependencies](#dependencies) - [Example Table of Contents](#example-table-of-contents) @@ -79,40 +91,87 @@ The benefits of restructuring the documentation according to these ideas include - The Docs section of the microsite will be divided into two top-level sections: Framework and Developer Portal. - The structure of the Table of Contents will align with the outline proposed in https://github.com/backstage/backstage/issues/21946. -### User Persona +### Personas -Documentation written for the user persona should be high level, assuming little to no technical knowledge. Concepts like the difference between Backstage Framework and Developer Portal should be explained at a high level to give Users a working knowledge, but should not be flushed out completely until we identify the user as a distinct user persona. Users may also be Administrators, Integrators or Product Managers, but documentation for Users should make no assumptions on that. +#### User -Example explanation of Backstage Framework vs Developer Portal: -"Backstage has two meanings, the Backstage Framework and the Backstage Developer Portal. Backstage Framework provides the tools you need to build your own developer portal and Backstage Developer Portal is your custom developer portal built using the Backstage Framework." +Users navigate the developer portal to access tools, information, and plugins essential for their daily tasks. They rely on Backstage to effortlessly find resources, utilize integrations, and connect with other tools and services within their ecosystem. Their interaction is predominantly with the frontend of the portal, where ease of use, accessibility, and relevant content discovery are critical. -### Administrator Persona +##### Documentation Style -Documentation written for this persona should be DevOps technical, assuming a strong DevOps background with the exception of a Getting Started section. While we should assume an overall technical knowledge, where possible we should link out to existing strong guides for the technologies we use, ex: PostgreSQL, Docker, Kubernetes, etc. The goal with administrator documentation is to give administrators a strong understanding of how to deploy and manage a Backstage Developer Portal, best practices, and possible tripping points. +Documentation for this persona should be about usability of the portal once it is running. For example: -Example explanation of Backstage Framework vs Developer Portal: -"Backstage has two meanings, the Backstage Framework and the Backstage Developer Portal. As an administrator, you will be interacting with Backstage Developer Portal primarily -- this is the running Backstage instance that you're managing. It is also useful to have a high level understanding of the Backstage Framework for mitigating issues or better understanding how to scale your Backstage Developer Portal." +- Understanding the mechanics of the Software Catalog +- Registering components +- Deleting components +- How the source of truth is the external tool linked through the plugins +- Understand dependencies relations and the overall schema of the catalog +- Using available scaffolder actions +- Customizing new workflows with available actions +- Searching +- Using available plugins +- Step by step tutorials -### Integrator Persona +#### Administrator -Documentation written for this persona should be software technical, assuming a strong software background with the exception of a Getting Started section. While we can assume an overall technical knowledge, where possible we should link out to useful guides for the technologies we use, ex: Node.js, express.js, React, etc. The goal with documentation written for integrators is to give them a strong understanding of where their work fits into their company's Backstage Developer Portal, orient them to get support from the open source community, and prepare them for continuing to deliver value for their Backstage Developer Portal. +Administrators are responsible for the behind-the-scenes technical setup and maintenance of the Backstage portal. This includes deploying the portal, configuring plugins, managing user access, and ensuring the security and performance of the system. They interact with both the frontend and backend, often using command-line tools, administrative dashboards, and configuration files to perform their tasks. -Example explanation of Backstage Framework vs Developer Portal: -"Backstage has two meanings, the Backstage Framework and the Backstage Developer Portal. As an integrator, most of your time will be spent working to deliver value on top of the Backstage Framework for your company's Backstage Developer Portal. This means creating plugins, theming your Backstage Developer Portal or adding integrations to internal data stores. You should have a strong understanding of the Backstage Framework and have a strong understaing of code that sits in your Backstage Developer Portal." +##### Documentation Style -### Business Stakeholder +Documentation written for this persona should be DevOps technical, assuming a strong DevOps background. The goal with administrator documentation is to give administrators a strong understanding of how to deploy and manage a Backstage Developer Portal, best practices. For example: -Documentation written for this persona should be strategic, assuming a strong background in business development and strategy. The goal for business documentation is to give a strong understanding of what Backstage Developer Portal can do for their company, how to deliver value quickly and continuously and guides for pitching or driving Backstage adoption. +- Installing and upgrading +- Configuring + - Authentication + - Plugins +- Ingesting data (users/groups/components, etc) +- Installing plugins +- Implement Git Flows for the Developer portal +- Creating Pipelines for Docs generation +- Troubleshooting -Example explanation of Backstage Framework vs Developer Portal: -"Backstage has two meanings, the Backstage Framework and the Backstage Developer Portal. As a business stakeholder, your time should be spent with your company's Backstage Developer Portal solely. This means understanding the value proposition of an IDP, what improving the developer experience at your company means and what a successful Backstage Developer Portal looks like. The Backstage Framework is the technical underpinning and will generally be invisible to you." +#### Integrator -## Contributor +Integrators actively work on extending and customizing Backstage. This includes developing new plugins, customizing the UI/UX, and integrating external services or data sources. Their work is deeply technical, involving coding, and engaging with the Backstage community for support and collaboration. They need a deep understanding of the Backstage architecture and APIs, working closely with both the framework's backend and frontend aspects. -Documentation written for this role should be technical, but should make no assumptions on technical strength. The goal with this documentation is to onboard new contributors to the technical stack and layout of the project, setting expectations for how to write code, documentation or generally contribute to the library. +##### Documentation Style -Example explanation of Backstage Framework vs Developer Portal: -"Backstage has two meanings, the Backstage Framework and the Backstage Developer Portal. You will work with both the Backstage Framework and Backstage Developer Portal. Backstage Framework is the technical framework for Backstage Developer Portal. The Backstage Developer Portal is the end user for your plugins, documentation or other contributions. As a contributor, you will be working to influence either the Backstage Framework or plugins and Backstage Developer Portals across the world may use your contributions." +Documentation written for this persona should be software technical, assuming a strong software background. While we can assume an overall technical knowledge, where possible we should link out to useful guides for the technologies we use, ex: Node.js, express.js, React, etc. The goal with documentation written for integrators is to give them a strong understanding of how to use the Backstage framework to build/evolve a company's Backstage Developer Portal, orient them to get support from the open source community, and prepare them for continuing to deliver value for their Backstage Developer Portal. For example: + +- API references +- Frontend and Backend systems +- Package architecture +- Extending the Software Catalog +- Creating custom themes +- Integrating new react components +- Building custom authentication providers/strategies +- Accessibility + +#### Contributor + +Contributors are involved in the development of the Backstage framework itself. They contribute to the core codebase, develop new features, fix bugs, create documentation and maintain the overall health of the project. They are deeply involved in the open-source community, collaborating with maintainers and other contributors to improve the framework and its ecosystem. + +##### Documentation Style + +The goal with documentation written for contributors is to give them a strong understanding of how to contribute to the Backstage framework, orient them to get support from the open source community, and prepare them for continuing to deliver value for the Backstage framework. For example: + +- Contributing to the Backstage framework +- Setting up a development environment +- Writing tests +- Writing documentation + +#### Business Stakeholder + +Business stakeholders use Backstage to align technical capabilities with business goals, monitoring how features and plugins support operational efficiency, developer satisfaction, and strategic objectives. They are involved in defining the strategy and measuring the impact of the developer portal on the organization. They need to navigate through dashboards, reports, and analytics within Backstage to gather insights and make informed decisions. + +##### Documentation Style + +Documentation written for this persona should be strategic, assuming a strong background in business development and strategy. The goal for business documentation is to give a strong understanding of what Backstage Developer Portal can do for their company, how to deliver value quickly and continuously and guides for pitching or driving Backstage adoption. For example: + +- Adoption use cases +- Adoption strategies +- Measuring success +- Case studies ## Release Plan From 735a88aa3a7784692d2d0c9fadc935e2eface976 Mon Sep 17 00:00:00 2001 From: sishwarya <63347232+sishwarya@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:42:29 +0530 Subject: [PATCH 005/251] Create deploy.yaml Signed-off-by: sishwarya <63347232+sishwarya@users.noreply.github.com> --- microsite/data/plugins/deploy.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 microsite/data/plugins/deploy.yaml diff --git a/microsite/data/plugins/deploy.yaml b/microsite/data/plugins/deploy.yaml new file mode 100644 index 0000000000..7d4481eeda --- /dev/null +++ b/microsite/data/plugins/deploy.yaml @@ -0,0 +1,10 @@ +--- +title: Digital.ai Deploy +author: Deploy +authorUrl: https://docs.digital.ai/category/deploy +category: Development +description: Deploy is an agentless deployment automation solution, enabling software development organizations to deploy, upgrade, and rollback complex applications to target environments! +documentation: https://github.com/coder/backstage-plugins/blob/main/plugins/backstage-plugin-coder/README.md +iconUrl: /img/deploy.png +npmPackageName: '@digital.ai/plugin-dai-deploy','@digital.ai/plugin-dai-deploy-backend' +addedDate: '2024-04-8' From f9e4a3d39df7a8ecbfd92e15c4bce65d1f415224 Mon Sep 17 00:00:00 2001 From: sishwarya <63347232+sishwarya@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:45:17 +0530 Subject: [PATCH 006/251] Update deploy.yaml Signed-off-by: sishwarya <63347232+sishwarya@users.noreply.github.com> --- microsite/data/plugins/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/deploy.yaml b/microsite/data/plugins/deploy.yaml index 7d4481eeda..d5359863f0 100644 --- a/microsite/data/plugins/deploy.yaml +++ b/microsite/data/plugins/deploy.yaml @@ -2,7 +2,7 @@ title: Digital.ai Deploy author: Deploy authorUrl: https://docs.digital.ai/category/deploy -category: Development +category: CI/CD description: Deploy is an agentless deployment automation solution, enabling software development organizations to deploy, upgrade, and rollback complex applications to target environments! documentation: https://github.com/coder/backstage-plugins/blob/main/plugins/backstage-plugin-coder/README.md iconUrl: /img/deploy.png From 01968f51495455044f05eac77db17c918dd4f184 Mon Sep 17 00:00:00 2001 From: sishwarya <63347232+sishwarya@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:02:59 +0530 Subject: [PATCH 007/251] Update deploy.yaml Signed-off-by: sishwarya <63347232+sishwarya@users.noreply.github.com> --- microsite/data/plugins/deploy.yaml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/microsite/data/plugins/deploy.yaml b/microsite/data/plugins/deploy.yaml index d5359863f0..c1caea4b48 100644 --- a/microsite/data/plugins/deploy.yaml +++ b/microsite/data/plugins/deploy.yaml @@ -1,10 +1,13 @@ --- -title: Digital.ai Deploy -author: Deploy -authorUrl: https://docs.digital.ai/category/deploy +title: Deploy +author: digital.ai +authorUrl: https://digital.ai/ category: CI/CD description: Deploy is an agentless deployment automation solution, enabling software development organizations to deploy, upgrade, and rollback complex applications to target environments! -documentation: https://github.com/coder/backstage-plugins/blob/main/plugins/backstage-plugin-coder/README.md +documentation: https://github.com/digital-ai/backstage-deploy/blob/main/README.md iconUrl: /img/deploy.png -npmPackageName: '@digital.ai/plugin-dai-deploy','@digital.ai/plugin-dai-deploy-backend' +npmPackageName: '@digital.ai/plugin-dai-deploy' +tags: + - ci + - cd addedDate: '2024-04-8' From 21caf84d50334e954354ce969034b38ab0556422 Mon Sep 17 00:00:00 2001 From: Ishwarya Surendrababu Date: Mon, 8 Apr 2024 17:37:23 +0530 Subject: [PATCH 008/251] updated the images and deploy.yaml Signed-off-by: Ishwarya Surendrababu --- microsite/data/plugins/deploy.yaml | 4 ++-- microsite/static/img/deploy.svg | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 microsite/static/img/deploy.svg diff --git a/microsite/data/plugins/deploy.yaml b/microsite/data/plugins/deploy.yaml index c1caea4b48..1e2394a920 100644 --- a/microsite/data/plugins/deploy.yaml +++ b/microsite/data/plugins/deploy.yaml @@ -3,9 +3,9 @@ title: Deploy author: digital.ai authorUrl: https://digital.ai/ category: CI/CD -description: Deploy is an agentless deployment automation solution, enabling software development organizations to deploy, upgrade, and rollback complex applications to target environments! +description: The plugin offers integration with Digital.ai Deploy and backstage components and services. It provide access to deployments and reports. documentation: https://github.com/digital-ai/backstage-deploy/blob/main/README.md -iconUrl: /img/deploy.png +iconUrl: /img/deploy.svg npmPackageName: '@digital.ai/plugin-dai-deploy' tags: - ci diff --git a/microsite/static/img/deploy.svg b/microsite/static/img/deploy.svg new file mode 100644 index 0000000000..d0becb98f1 --- /dev/null +++ b/microsite/static/img/deploy.svg @@ -0,0 +1,7 @@ + + + + + + + From 2ab814f1246b1d0d50b81f20f6acf2dc8e9e93d0 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Wed, 10 Apr 2024 23:04:50 +0200 Subject: [PATCH 009/251] add more known styles Signed-off-by: Juan Pablo Garcia Ripa --- .../no-top-level-material-ui-4-imports.js | 18 +++++++++++++++++- .../no-top-level-material-ui-4-imports.test.ts | 8 +++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/rules/no-top-level-material-ui-4-imports.js b/packages/eslint-plugin/rules/no-top-level-material-ui-4-imports.js index 77adcad215..fa13c0367e 100644 --- a/packages/eslint-plugin/rules/no-top-level-material-ui-4-imports.js +++ b/packages/eslint-plugin/rules/no-top-level-material-ui-4-imports.js @@ -27,7 +27,23 @@ */ const KNOWN_STYLES = [ - // TODO: add exports from colorManipulator and transitions + // colorManipulator + 'hexToRgb', + 'rgbToHex', + 'hslToRgb', + 'decomposeColor', + 'recomposeColor', + 'getContrastRatio', + 'getLuminance', + 'emphasize', + 'fade', + 'alpha', + 'darken', + 'lighten', + // transitions + 'easing', + 'duration', + // styles 'createTheme', 'unstable_createMuiStrictModeTheme', 'createMuiTheme', diff --git a/packages/eslint-plugin/src/no-top-level-material-ui-4-imports.test.ts b/packages/eslint-plugin/src/no-top-level-material-ui-4-imports.test.ts index 2d29c2f9e9..0fe8b09364 100644 --- a/packages/eslint-plugin/src/no-top-level-material-ui-4-imports.test.ts +++ b/packages/eslint-plugin/src/no-top-level-material-ui-4-imports.test.ts @@ -93,6 +93,8 @@ import SvgIcon, { SvgIconProps } from '@material-ui/core/SvgIcon';`, ThemeProvider, WithStyles, Tooltip as MaterialTooltip, + alpha, + easing } from '@material-ui/core';`, errors: [{ messageId: 'topLevelImport' }], output: `import Box from '@material-ui/core/Box'; @@ -101,7 +103,7 @@ import DialogContent from '@material-ui/core/DialogContent'; import DialogTitle from '@material-ui/core/DialogTitle'; import Grid from '@material-ui/core/Grid'; import MaterialTooltip from '@material-ui/core/Tooltip'; -import { makeStyles, ThemeProvider, WithStyles } from '@material-ui/core/styles';`, +import { makeStyles, ThemeProvider, WithStyles, alpha, easing } from '@material-ui/core/styles';`, }, { code: `import { Box, Button, makeStyles } from '@material-ui/core';`, @@ -111,11 +113,11 @@ import Button from '@material-ui/core/Button'; import { makeStyles } from '@material-ui/core/styles';`, }, { - code: `import { Paper, Typography, styled, withStyles } from '@material-ui/core';`, + code: `import { Paper, Typography, styled, withStyles, alpha, duration} from '@material-ui/core';`, errors: [{ messageId: 'topLevelImport' }], output: `import Paper from '@material-ui/core/Paper'; import Typography from '@material-ui/core/Typography'; -import { styled, withStyles } from '@material-ui/core/styles';`, +import { styled, withStyles, alpha, duration } from '@material-ui/core/styles';`, }, { code: `import { styled } from '@material-ui/core';`, From c56cfd89906d28a40e43737a7f1cde386f162e3e Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Wed, 10 Apr 2024 23:09:21 +0200 Subject: [PATCH 010/251] remove harcoded module Signed-off-by: Juan Pablo Garcia Ripa --- .../rules/no-top-level-material-ui-4-imports.js | 8 ++++---- .../src/no-top-level-material-ui-4-imports.test.ts | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/rules/no-top-level-material-ui-4-imports.js b/packages/eslint-plugin/rules/no-top-level-material-ui-4-imports.js index fa13c0367e..adf04492b4 100644 --- a/packages/eslint-plugin/rules/no-top-level-material-ui-4-imports.js +++ b/packages/eslint-plugin/rules/no-top-level-material-ui-4-imports.js @@ -204,7 +204,7 @@ module.exports = { if (specifier.emitProp && !specifier.emitComponent) { const replacement = `import { ${getNamedImportValue( specifier, - )} } from '@material-ui/core/${specifier.componentValue}';`; + )} } from '${node.source.value}/${specifier.componentValue}';`; replacements.push(replacement); } @@ -213,9 +213,9 @@ module.exports = { replacements.push( `import ${ specifier.componentAlias ?? specifier.componentValue - }, { ${getNamedImportValue( - specifier, - )} } from '@material-ui/core/${specifier.componentValue}';`, + }, { ${getNamedImportValue(specifier)} } from '${ + node.source.value + }/${specifier.componentValue}';`, ); } } diff --git a/packages/eslint-plugin/src/no-top-level-material-ui-4-imports.test.ts b/packages/eslint-plugin/src/no-top-level-material-ui-4-imports.test.ts index 0fe8b09364..fdde615c3e 100644 --- a/packages/eslint-plugin/src/no-top-level-material-ui-4-imports.test.ts +++ b/packages/eslint-plugin/src/no-top-level-material-ui-4-imports.test.ts @@ -154,5 +154,12 @@ import { styled, withStyles, alpha, duration } from '@material-ui/core/styles';` errors: [{ messageId: 'topLevelImport' }], output: `import { styled as s } from '@material-ui/core/styles';`, }, + { + code: `import { TreeItem, TreeItemProps, TreeView, AlertProps } from '@material-ui/lab';`, + errors: [{ messageId: 'topLevelImport' }], + output: `import TreeItem, { TreeItemProps } from '@material-ui/lab/TreeItem'; +import TreeView from '@material-ui/lab/TreeView'; +import { AlertProps } from '@material-ui/lab/Alert';`, + }, ], }); From d76cb295d4018bd1fb0a4667dd45de011429d5fc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 14 Apr 2024 20:36:23 +0000 Subject: [PATCH 011/251] chore(deps): update dependency ts-morph to v22 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .changeset/renovate-228c530.md | 5 ++++ plugins/bitbucket-cloud-common/package.json | 2 +- yarn.lock | 30 ++++++++++----------- 3 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 .changeset/renovate-228c530.md diff --git a/.changeset/renovate-228c530.md b/.changeset/renovate-228c530.md new file mode 100644 index 0000000000..e1838b3481 --- /dev/null +++ b/.changeset/renovate-228c530.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-bitbucket-cloud-common': patch +--- + +Updated dependency `ts-morph` to `^22.0.0`. diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index 12bf713f11..4935aa3347 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -44,6 +44,6 @@ "@backstage/cli": "workspace:^", "@openapitools/openapi-generator-cli": "^2.4.26", "msw": "^1.0.0", - "ts-morph": "^21.0.0" + "ts-morph": "^22.0.0" } } diff --git a/yarn.lock b/yarn.lock index 6420fa6e3a..f59e37375c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5389,7 +5389,7 @@ __metadata: "@openapitools/openapi-generator-cli": ^2.4.26 cross-fetch: ^4.0.0 msw: ^1.0.0 - ts-morph: ^21.0.0 + ts-morph: ^22.0.0 languageName: unknown linkType: soft @@ -18188,15 +18188,15 @@ __metadata: languageName: node linkType: hard -"@ts-morph/common@npm:~0.22.0": - version: 0.22.0 - resolution: "@ts-morph/common@npm:0.22.0" +"@ts-morph/common@npm:~0.23.0": + version: 0.23.0 + resolution: "@ts-morph/common@npm:0.23.0" dependencies: fast-glob: ^3.3.2 minimatch: ^9.0.3 mkdirp: ^3.0.1 path-browserify: ^1.0.1 - checksum: e549facfff2a68eeef4e3e2c4183e7216a02b57e62cdfe60ca15d5fdee24770bd3b5b6d1a0388cfce7b4dfaeb0ebe31ffa40585e36b9fb7948aea8081fa73769 + checksum: 96463742ec1114900901ded8aecc2c9664b20119454a56c896042e6a5e5b153af1d986467362d737ed0130506aeac9655731922dc8c4e851a16f9c1a8a8099b4 languageName: node linkType: hard @@ -23566,10 +23566,10 @@ __metadata: languageName: node linkType: hard -"code-block-writer@npm:^12.0.0": - version: 12.0.0 - resolution: "code-block-writer@npm:12.0.0" - checksum: 9f6505a4d668c9131c6f3f686359079439e66d5f50c236614d52fcfa53aeb0bc615b2c6c64ef05b5511e3b0433ccfd9f7756ad40eb3b9298af6a7d791ab1981d +"code-block-writer@npm:^13.0.1": + version: 13.0.1 + resolution: "code-block-writer@npm:13.0.1" + checksum: 678b740d1723c7cc3c5addcbc1a91d9a7a3f033510eb8e0639154fcaae456c80630dbd40d16aefdffaf3edb5ffb352d7d46f195f69c8be692c4d7debb1dc7933 languageName: node linkType: hard @@ -44024,13 +44024,13 @@ __metadata: languageName: node linkType: hard -"ts-morph@npm:^21.0.0": - version: 21.0.1 - resolution: "ts-morph@npm:21.0.1" +"ts-morph@npm:^22.0.0": + version: 22.0.0 + resolution: "ts-morph@npm:22.0.0" dependencies: - "@ts-morph/common": ~0.22.0 - code-block-writer: ^12.0.0 - checksum: f8e6acd4cdb2842af47ccf4e8900dc3f230f20c3b0d28e1e8b58c395b0a16d7b3e03ef56f29da3fdb861c50e22eb52524e0fc4bfca0fde8448f81b8f4f6aa157 + "@ts-morph/common": ~0.23.0 + code-block-writer: ^13.0.1 + checksum: 7bf0ec8cc9ab2a4ce528ec249634db315caa01b5783259484fd91ac0dae2f05b94d9c53ba236939fad2b8ab882e7fce1c82a689ab7c9a4a9dcec5c1160d77264 languageName: node linkType: hard From 9b7dfe7ea98bc6bd30daaa511b96e47b08c36db0 Mon Sep 17 00:00:00 2001 From: Kiss Miklos Date: Wed, 17 Apr 2024 17:04:13 +0200 Subject: [PATCH 012/251] improve TechDocsSearch Signed-off-by: Kiss Miklos --- plugins/techdocs/src/search/components/TechDocsSearch.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs/src/search/components/TechDocsSearch.tsx b/plugins/techdocs/src/search/components/TechDocsSearch.tsx index 665f99c1bb..ce77d547db 100644 --- a/plugins/techdocs/src/search/components/TechDocsSearch.tsx +++ b/plugins/techdocs/src/search/components/TechDocsSearch.tsx @@ -63,6 +63,7 @@ const TechDocsSearchBar = (props: TechDocsSearchProps) => { const navigate = useNavigate(); const { setFilters, + term, result: { loading, value: searchVal }, } = useSearch(); const [options, setOptions] = useState([]); @@ -109,7 +110,7 @@ const TechDocsSearchBar = (props: TechDocsSearchProps) => { ''} filterOptions={x => { return x; // This is needed to get renderOption to be called after options change. Bug in material-ui? @@ -117,7 +118,7 @@ const TechDocsSearchBar = (props: TechDocsSearchProps) => { onClose={() => { setOpen(false); }} - onFocus={() => { + onOpen={() => { setOpen(true); }} onChange={handleSelection} From 1256d8813d3fc5ad0eca36d78b66825ca6c48b1e Mon Sep 17 00:00:00 2001 From: Kiss Miklos Date: Wed, 17 Apr 2024 18:37:35 +0200 Subject: [PATCH 013/251] add changeset Signed-off-by: Kiss Miklos --- .changeset/slimy-fans-raise.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/slimy-fans-raise.md diff --git a/.changeset/slimy-fans-raise.md b/.changeset/slimy-fans-raise.md new file mode 100644 index 0000000000..782ef4bc82 --- /dev/null +++ b/.changeset/slimy-fans-raise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Fix weird opening behaviour of the component. From 88191bb54ebf9e118f3c5fbbd2a7ab7b1e669bd6 Mon Sep 17 00:00:00 2001 From: NIKUNJ LALITKUMAR HUDKA Date: Wed, 17 Apr 2024 17:39:41 -0300 Subject: [PATCH 014/251] bugfix: Proper error thrown in plugin-azure-devops-backend when gitRepository is not found. Signed-off-by: NIKUNJ LALITKUMAR HUDKA --- .changeset/three-sheep-remember.md | 5 +++ .../src/api/AzureDevOpsApi.test.ts | 37 +++++++++++++++++++ .../src/api/AzureDevOpsApi.ts | 20 ++++++++++ 3 files changed, 62 insertions(+) create mode 100644 .changeset/three-sheep-remember.md diff --git a/.changeset/three-sheep-remember.md b/.changeset/three-sheep-remember.md new file mode 100644 index 0000000000..51ffc8e3db --- /dev/null +++ b/.changeset/three-sheep-remember.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops-backend': minor +--- + +Fixed bug in plugin-azure-devops-backend where proper error was not thrown when gitRepository was not found. diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts index 174c5ff6cd..a056beee02 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts @@ -465,6 +465,43 @@ describe('AzureDevOpsApi', () => { ]); }); + it('should throw error when gitRepository is undefined', async () => { + const mockApi = { + getGitApi: jest.fn().mockReturnValue({}), + serverUrl: 'serverUrl', + }; + + (WebApi as unknown as jest.Mock).mockImplementation(() => mockApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, + }); + + const pullRequestOptions: PullRequestOptions = { + top: 10, + status: PullRequestStatus.Active, + }; + + api.getGitRepository = jest.fn().mockResolvedValue(undefined); + + const temp = async () => { + try { + await api.getPullRequests('project', 'repo', pullRequestOptions); + return null; + } catch (error) { + return error; + } + }; + + const error = await temp(); + + expect(error).toHaveProperty( + 'message', + 'No repository found for Project "project" with Repository "repo" on host "undefined" under organization "undefined".', + ); + }); + it('should get build definitions', async () => { const mockBuilds: Build[] = [ { diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index 4ed54e63eb..468d6a9d88 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -231,6 +231,11 @@ export class AzureDevOpsApi { host, org, ); + if (!gitRepository) { + throw new Error( + `No repository found for Project "${projectName}" with Repository "${repoName}" on host "${host}" under organization "${org}".`, + ); + } const buildList = await this.getBuildList( projectName, gitRepository.id as string, @@ -262,6 +267,11 @@ export class AzureDevOpsApi { host, org, ); + if (!gitRepository) { + throw new Error( + `No repository found for Project "${projectName}" with Repository "${repoName}" on host "${host}" under organization "${org}".`, + ); + } const webApi = await this.getWebApi(host, org); const client = await webApi.getGitApi(); const tagRefs: GitRef[] = await client.getRefs( @@ -304,6 +314,11 @@ export class AzureDevOpsApi { host, org, ); + if (!gitRepository) { + throw new Error( + `No repository found for Project "${projectName}" with Repository "${repoName}" on host "${host}" under organization "${org}".`, + ); + } const webApi = await this.getWebApi(host, org); const client = await webApi.getGitApi(); const searchCriteria: GitPullRequestSearchCriteria = { @@ -514,6 +529,11 @@ export class AzureDevOpsApi { host, org, ); + if (!gitRepository) { + throw new Error( + `No repository found for Project "${projectName}" with Repository "${repoName}" on host "${host}" under organization "${org}".`, + ); + } repoId = gitRepository.id; } From bdf5d3c8e726ea886a27d039147fe1c943173905 Mon Sep 17 00:00:00 2001 From: Ishwarya Surendrababu Date: Wed, 17 Apr 2024 09:15:11 +0530 Subject: [PATCH 015/251] updating docs link Signed-off-by: Ishwarya Surendrababu --- microsite/data/plugins/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/deploy.yaml b/microsite/data/plugins/deploy.yaml index 1e2394a920..a619a7ba0b 100644 --- a/microsite/data/plugins/deploy.yaml +++ b/microsite/data/plugins/deploy.yaml @@ -4,7 +4,7 @@ author: digital.ai authorUrl: https://digital.ai/ category: CI/CD description: The plugin offers integration with Digital.ai Deploy and backstage components and services. It provide access to deployments and reports. -documentation: https://github.com/digital-ai/backstage-deploy/blob/main/README.md +documentation: https://docs.digital.ai/bundle/devops-deploy-version-v.24.1/page/deploy/concept/xl-deploy-backstage-overview.html iconUrl: /img/deploy.svg npmPackageName: '@digital.ai/plugin-dai-deploy' tags: From dbde8c0d6fa8d5937faad7bb347c4ab5440143b0 Mon Sep 17 00:00:00 2001 From: NIKUNJ LALITKUMAR HUDKA Date: Fri, 19 Apr 2024 09:46:44 -0300 Subject: [PATCH 016/251] feat: EntityListComponent now uses Catalog Presentation API Signed-off-by: NIKUNJ LALITKUMAR HUDKA --- plugins/catalog-import/package.json | 1 + .../EntityListComponent.tsx | 43 ++++-- .../StepPrepareSelectLocations.test.tsx | 126 ++++++++++++------ 3 files changed, 118 insertions(+), 52 deletions(-) diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 67215f6a74..8a882206a3 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -78,6 +78,7 @@ "@backstage/cli": "workspace:^", "@backstage/core-app-api": "workspace:^", "@backstage/dev-utils": "workspace:^", + "@backstage/plugin-catalog": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/dom": "^10.0.0", "@testing-library/jest-dom": "^6.0.0", diff --git a/plugins/catalog-import/src/components/EntityListComponent/EntityListComponent.tsx b/plugins/catalog-import/src/components/EntityListComponent/EntityListComponent.tsx index ef57fad204..9c4af562e5 100644 --- a/plugins/catalog-import/src/components/EntityListComponent/EntityListComponent.tsx +++ b/plugins/catalog-import/src/components/EntityListComponent/EntityListComponent.tsx @@ -14,11 +14,15 @@ * limitations under the License. */ -import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; -import { useApp } from '@backstage/core-plugin-api'; +import { + Entity, + CompoundEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { useApi, useApp } from '@backstage/core-plugin-api'; import { EntityRefLink, - humanizeEntityRef, + entityPresentationApiRef, } from '@backstage/plugin-catalog-react'; import Collapse from '@material-ui/core/Collapse'; import IconButton from '@material-ui/core/IconButton'; @@ -38,12 +42,6 @@ const useStyles = makeStyles(theme => ({ }, })); -function sortEntities(entities: Array) { - return entities.sort((a, b) => - humanizeEntityRef(a).localeCompare(humanizeEntityRef(b)), - ); -} - /** * Props for {@link EntityListComponent}. * @@ -78,7 +76,7 @@ export const EntityListComponent = (props: EntityListComponentProps) => { const app = useApp(); const classes = useStyles(); - + const entityPresentationApi = useApi(entityPresentationApiRef); const [expandedUrls, setExpandedUrls] = useState([]); const handleClick = (url: string) => { @@ -87,6 +85,17 @@ export const EntityListComponent = (props: EntityListComponentProps) => { ); }; + function sortEntities(entities: Array) { + return entities.sort((a, b) => + entityPresentationApi + .forEntity(stringifyEntityRef(a)) + .snapshot.entityRef.localeCompare( + entityPresentationApi.forEntity(stringifyEntityRef(b)).snapshot + .entityRef, + ), + ); + } + return ( {firstListItem} @@ -129,7 +138,11 @@ export const EntityListComponent = (props: EntityListComponentProps) => { ); return ( { : {})} > {Icon && } - + ); })} diff --git a/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx b/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx index b5efd3fbbf..e5a4174859 100644 --- a/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx @@ -14,14 +14,22 @@ * limitations under the License. */ -import { renderInTestApp } from '@backstage/test-utils'; +import { TestApiProvider, renderInTestApp } from '@backstage/test-utils'; import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { AnalyzeResult } from '../../api'; import { StepPrepareSelectLocations } from './StepPrepareSelectLocations'; +import { + CatalogApi, + catalogApiRef, + entityPresentationApiRef, +} from '@backstage/plugin-catalog-react'; +import { DefaultEntityPresentationApi } from '@backstage/plugin-catalog'; +import { Entity } from '@backstage/catalog-model'; describe('', () => { + let entities: Entity[]; const analyzeResult = { type: 'locations', locations: [ @@ -53,17 +61,43 @@ describe('', () => { ], } as Extract; + const catalogApi: jest.Mocked = { + getLocationById: jest.fn(), + getEntityByName: jest.fn(), + getEntities: jest.fn(async () => ({ items: entities })), + addLocation: jest.fn(), + getLocationByRef: jest.fn(), + removeEntityByUid: jest.fn(), + } as any; + let Wrapper: React.ComponentType>; + beforeEach(() => { jest.resetAllMocks(); + catalogApi.getEntities.mockResolvedValue({ items: entities }); + Wrapper = ({ children }: { children?: React.ReactNode }) => ( + + {children} + + ); }); it('renders display locations to be added', async () => { await renderInTestApp( - undefined} - onGoBack={() => undefined} - />, + + undefined} + onGoBack={() => undefined} + /> + , ); expect(screen.getByText('url-1')).toBeInTheDocument(); @@ -96,11 +130,13 @@ describe('', () => { } as Extract; await renderInTestApp( - undefined} - onGoBack={() => undefined} - />, + + undefined} + onGoBack={() => undefined} + /> + , ); expect(screen.getByText(/my-target/)).toBeInTheDocument(); @@ -112,11 +148,13 @@ describe('', () => { it('should select and deselect all', async () => { await renderInTestApp( - undefined} - onGoBack={() => undefined} - />, + + undefined} + onGoBack={() => undefined} + /> + , ); const checkboxes = screen.getAllByRole('checkbox'); @@ -144,15 +182,17 @@ describe('', () => { it('should preselect prepared locations', async () => { await renderInTestApp( - undefined} - onGoBack={() => undefined} - />, + + undefined} + onGoBack={() => undefined} + /> + , ); const checkboxes = screen.getAllByRole('checkbox'); @@ -164,11 +204,13 @@ describe('', () => { it('should select items', async () => { await renderInTestApp( - undefined} - onGoBack={() => undefined} - />, + + undefined} + onGoBack={() => undefined} + /> + , ); const checkboxes = screen.getAllByRole('checkbox'); @@ -193,11 +235,13 @@ describe('', () => { const onGoBack = jest.fn(); await renderInTestApp( - undefined} - onGoBack={onGoBack} - />, + + undefined} + onGoBack={onGoBack} + /> + , ); await act(async () => { @@ -211,11 +255,13 @@ describe('', () => { const onPrepare = jest.fn(); await renderInTestApp( - undefined} - />, + + undefined} + /> + , ); const checkboxes = screen.getAllByRole('checkbox'); From 026c199ead6734043e805fecb62792197d9b152d Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Fri, 19 Apr 2024 20:30:15 +0530 Subject: [PATCH 017/251] removing react-text-truncate Signed-off-by: npiyush97 --- .changeset/strange-doors-glow.md | 5 +++ .../OverflowTooltip/OverflowTooltip.tsx | 34 +++++++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 .changeset/strange-doors-glow.md diff --git a/.changeset/strange-doors-glow.md b/.changeset/strange-doors-glow.md new file mode 100644 index 0000000000..71dcc375c0 --- /dev/null +++ b/.changeset/strange-doors-glow.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Removing react-text-truncate with css styles. diff --git a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx index da489725e4..8585da1013 100644 --- a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx +++ b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ /* * Copyright 2020 The Backstage Authors * @@ -19,6 +20,7 @@ import Tooltip, { TooltipProps } from '@material-ui/core/Tooltip'; import React, { useState } from 'react'; import TextTruncate, { TextTruncateProps } from 'react-text-truncate'; import { useIsMounted } from '@react-hookz/web'; +import Typography from '@material-ui/core/Typography'; type Props = { text: TextTruncateProps['text']; @@ -35,33 +37,35 @@ const useStyles = makeStyles( container: { overflow: 'visible !important', }, + typo: { + // width: 200, + display: 'inline-block', + overflow: 'hidden', + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + }, }, { name: 'BackstageOverflowTooltip' }, ); export function OverflowTooltip(props: Props) { - const [hover, setHover] = useState(false); - const isMounted = useIsMounted(); + // const [hover, setHover] = useState(false); + // const isMounted = useIsMounted(); const classes = useStyles(); - - const handleToggled = (truncated: boolean) => { - if (isMounted()) { - setHover(truncated); - } - }; + console.log(classes); + // const handleToggled = (truncated: boolean) => { + // if (isMounted()) { + // setHover(truncated); + // } + // }; return ( - + {props.text} ); } From ad0ee563836559f34963f2ac7639aca1cf1679ad Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Fri, 19 Apr 2024 20:56:19 +0530 Subject: [PATCH 018/251] little cleanup Signed-off-by: npiyush97 --- .../OverflowTooltip/OverflowTooltip.tsx | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx index 8585da1013..b7a33f97fc 100644 --- a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx +++ b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx @@ -17,15 +17,11 @@ import { makeStyles } from '@material-ui/core/styles'; import Tooltip, { TooltipProps } from '@material-ui/core/Tooltip'; -import React, { useState } from 'react'; -import TextTruncate, { TextTruncateProps } from 'react-text-truncate'; -import { useIsMounted } from '@react-hookz/web'; +import React from 'react'; import Typography from '@material-ui/core/Typography'; type Props = { - text: TextTruncateProps['text']; - line?: TextTruncateProps['line']; - element?: TextTruncateProps['element']; + text: string; title?: TooltipProps['title']; placement?: TooltipProps['placement']; }; @@ -38,7 +34,6 @@ const useStyles = makeStyles( overflow: 'visible !important', }, typo: { - // width: 200, display: 'inline-block', overflow: 'hidden', whiteSpace: 'nowrap', @@ -49,21 +44,12 @@ const useStyles = makeStyles( ); export function OverflowTooltip(props: Props) { - // const [hover, setHover] = useState(false); - // const isMounted = useIsMounted(); const classes = useStyles(); - console.log(classes); - // const handleToggled = (truncated: boolean) => { - // if (isMounted()) { - // setHover(truncated); - // } - // }; return ( {props.text} From 7c234d0cead00265f05cbc81a6a84b5bd07d1b5f Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Fri, 19 Apr 2024 21:25:00 +0530 Subject: [PATCH 019/251] added style maxwidth 200 and props Signed-off-by: npiyush97 --- packages/core-components/api-report.md | 1 - packages/core-components/package.json | 46 +++++++++---------- .../OverflowTooltip.stories.tsx | 8 +--- .../OverflowTooltip/OverflowTooltip.tsx | 3 +- 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 7fd706db43..06676ab429 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -47,7 +47,6 @@ import { StyledComponentProps } from '@material-ui/core/styles/withStyles'; import { StyleRules } from '@material-ui/styles'; import { StyleRules as StyleRules_2 } from '@material-ui/core/styles/withStyles'; import { TabProps } from '@material-ui/core/Tab'; -import { TextTruncateProps } from 'react-text-truncate'; import { Theme } from '@material-ui/core/styles'; import { TooltipProps } from '@material-ui/core/Tooltip'; import { WithStyles } from '@material-ui/core/styles'; diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 9f632bcc7f..cbdb256c65 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,31 +1,32 @@ { "name": "@backstage/core-components", - "description": "Core components used by Backstage plugins and apps", "version": "0.14.4", - "publishConfig": { - "access": "public" - }, + "description": "Core components used by Backstage plugins and apps", "backstage": { "role": "web-library" }, + "publishConfig": { + "access": "public" + }, + "keywords": [ + "backstage" + ], "homepage": "https://backstage.io", "repository": { "type": "git", "url": "https://github.com/backstage/backstage", "directory": "packages/core-components" }, - "keywords": [ - "backstage" - ], "license": "Apache-2.0", - "main": "src/index.ts", - "types": "src/index.ts", + "sideEffects": false, "exports": { ".": "./src/index.ts", "./alpha": "./src/alpha.ts", "./testUtils": "./src/testUtils.ts", "./package.json": "./package.json" }, + "main": "src/index.ts", + "types": "src/index.ts", "typesVersions": { "*": { "alpha": [ @@ -39,15 +40,18 @@ ] } }, - "sideEffects": false, + "files": [ + "dist", + "config.d.ts" + ], "scripts": { "build": "backstage-cli package build", + "clean": "backstage-cli package clean", "lint": "backstage-cli package lint", - "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "clean": "backstage-cli package clean", - "start": "backstage-cli package start" + "start": "backstage-cli package start", + "test": "backstage-cli package test" }, "dependencies": { "@backstage/config": "workspace:^", @@ -63,7 +67,6 @@ "@react-hookz/web": "^24.0.0", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "@types/react-sparklines": "^1.7.0", - "@types/react-text-truncate": "^0.14.0", "ansi-regex": "^6.0.1", "classnames": "^2.2.6", "d3-selection": "^3.0.0", @@ -82,7 +85,6 @@ "react-markdown": "^8.0.0", "react-sparklines": "^1.7.0", "react-syntax-highlighter": "^15.4.5", - "react-text-truncate": "^0.19.0", "react-use": "^17.3.2", "react-virtualized-auto-sizer": "^1.0.11", "react-window": "^1.8.6", @@ -90,11 +92,6 @@ "zen-observable": "^0.10.0", "zod": "^3.22.4" }, - "peerDependencies": { - "react": "^16.13.1 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0", - "react-router-dom": "6.0.0-beta.0 || ^6.3.0" - }, "devDependencies": { "@backstage/app-defaults": "workspace:^", "@backstage/cli": "workspace:^", @@ -120,9 +117,10 @@ "history": "^5.0.0", "msw": "^1.0.0" }, - "files": [ - "dist", - "config.d.ts" - ], + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0", + "react-router-dom": "6.0.0-beta.0 || ^6.3.0" + }, "configSchema": "config.d.ts" } diff --git a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx index 5ab5ba7dc4..a302a0a7e3 100644 --- a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx +++ b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx @@ -33,16 +33,12 @@ export const Default = () => ( export const MultiLine = () => ( - + ); export const DifferentTitle = () => ( - + ); diff --git a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx index b7a33f97fc..12de79644f 100644 --- a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx +++ b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx @@ -21,7 +21,7 @@ import React from 'react'; import Typography from '@material-ui/core/Typography'; type Props = { - text: string; + text?: string | undefined; title?: TooltipProps['title']; placement?: TooltipProps['placement']; }; @@ -34,6 +34,7 @@ const useStyles = makeStyles( overflow: 'visible !important', }, typo: { + maxWidth: 200, display: 'inline-block', overflow: 'hidden', whiteSpace: 'nowrap', From 3e39b436e5a1cf837335acb57f6d7811e7683ba3 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Fri, 19 Apr 2024 21:26:54 +0530 Subject: [PATCH 020/251] add yarn.lock Signed-off-by: npiyush97 --- yarn.lock | 2 -- 1 file changed, 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0a27eecdea..8ef4c2bab1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3923,7 +3923,6 @@ __metadata: "@types/react-helmet": ^6.1.0 "@types/react-sparklines": ^1.7.0 "@types/react-syntax-highlighter": ^15.0.0 - "@types/react-text-truncate": ^0.14.0 "@types/react-virtualized-auto-sizer": ^1.0.1 "@types/react-window": ^1.8.5 "@types/zen-observable": ^0.8.0 @@ -3948,7 +3947,6 @@ __metadata: react-markdown: ^8.0.0 react-sparklines: ^1.7.0 react-syntax-highlighter: ^15.4.5 - react-text-truncate: ^0.19.0 react-use: ^17.3.2 react-virtualized-auto-sizer: ^1.0.11 react-window: ^1.8.6 From d74a191b6c269ae9bcd99769d6d2a1d5dc902ff4 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Fri, 19 Apr 2024 21:33:31 +0530 Subject: [PATCH 021/251] missing this one Signed-off-by: npiyush97 --- plugins/catalog-react/src/components/EntityTable/columns.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/catalog-react/src/components/EntityTable/columns.tsx b/plugins/catalog-react/src/components/EntityTable/columns.tsx index 292235c78c..3357f1dded 100644 --- a/plugins/catalog-react/src/components/EntityTable/columns.tsx +++ b/plugins/catalog-react/src/components/EntityTable/columns.tsx @@ -141,7 +141,6 @@ export const columnFactories = Object.freeze({ ), }; From ff03fd55dee6b85b63ee79bdd8a1930433b46f8a Mon Sep 17 00:00:00 2001 From: NIKUNJ LALITKUMAR HUDKA Date: Sat, 20 Apr 2024 12:03:21 -0300 Subject: [PATCH 022/251] feat: allow promise in EntityPresentationApi for all the asynchronous processes working under the hood Signed-off-by: NIKUNJ LALITKUMAR HUDKA --- .../EntityPresentationApi.ts | 5 ++++ .../DefaultEntityPresentationApi.ts | 29 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts index 945bce1d27..de5449917a 100644 --- a/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts @@ -109,6 +109,11 @@ export interface EntityRefPresentation { * elsewhere. */ update$?: Observable; + + /* The `promise` property in the `EntityRefPresentation` interface is defining a property named + `promise` that holds a promise. This promise resolves to an array of + `EntityRefPresentationSnapshot` objects. */ + promise: Promise; } /** diff --git a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts index 82862be435..bbd5540d15 100644 --- a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts +++ b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts @@ -298,10 +298,37 @@ export class DefaultEntityPresentationApi implements EntityPresentationApi { }; }); - return { + const entityRefPresentation: EntityRefPresentation = { snapshot: initialSnapshot, update$: observable, + get promise() { + return new Promise( + (resolve, reject) => { + if (!observable) { + resolve([initialSnapshot]); + } else { + const res: EntityRefPresentationSnapshot[] = []; + const subscription = observable.subscribe({ + next: snapshot => { + res.push(snapshot); + }, + error: error => { + initialSnapshot = { + primaryTitle: entityRef, + entityRef: entityRef, + }; + }, + complete() { + subscription.unsubscribe(); + resolve(res); + }, + }); + } + }, + ); + }, }; + return entityRefPresentation; } #getEntityForInitialRender(entityOrRef: Entity | string): { From 929cb2611873cee3d562530e0313b2ca9c727aa3 Mon Sep 17 00:00:00 2001 From: NIKUNJ LALITKUMAR HUDKA Date: Sat, 20 Apr 2024 12:09:43 -0300 Subject: [PATCH 023/251] feat: MultiEntityPicker uses entityPresentationApi to display entity instead of humanizeEntityRef Signed-off-by: NIKUNJ LALITKUMAR HUDKA --- .../MultiEntityPicker.test.tsx | 17 ++++- .../MultiEntityPicker/MultiEntityPicker.tsx | 76 +++++++++++-------- 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx index bc0a5ed32d..4be07b139d 100644 --- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx @@ -16,7 +16,11 @@ import { CATALOG_FILTER_EXISTS } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; -import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react'; +import { + CatalogApi, + catalogApiRef, + entityPresentationApiRef, +} from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import { fireEvent, screen } from '@testing-library/react'; @@ -24,6 +28,7 @@ import React from 'react'; import { MultiEntityPicker } from './MultiEntityPicker'; import { MultiEntityPickerProps } from './schema'; import { ScaffolderRJSFFieldProps as FieldProps } from '@backstage/plugin-scaffolder-react'; +import { DefaultEntityPresentationApi } from '@backstage/plugin-catalog'; const makeEntity = (kind: string, namespace: string, name: string): Entity => ({ apiVersion: 'scaffolder.backstage.io/v1beta3', @@ -59,7 +64,15 @@ describe('', () => { ]; Wrapper = ({ children }: { children?: React.ReactNode }) => ( - + {children} ); diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx index d9804a8cba..0a0fcfdc2b 100644 --- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx @@ -25,7 +25,9 @@ import { import { useApi } from '@backstage/core-plugin-api'; import { catalogApiRef, - humanizeEntityRef, + entityPresentationApiRef, + EntityRefPresentationSnapshot, + EntityDisplayName, } from '@backstage/plugin-catalog-react'; import TextField from '@material-ui/core/TextField'; import FormControl from '@material-ui/core/FormControl'; @@ -64,40 +66,35 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => { uiSchema['ui:options']?.defaultNamespace || undefined; const catalogApi = useApi(catalogApiRef); - + const entityPresentationApi = useApi(entityPresentationApiRef); const { value: entities, loading } = useAsync(async () => { const { items } = await catalogApi.getEntities( catalogFilter ? { filter: catalogFilter } : undefined, ); - return items; + const primaryTitles: string[] = []; + for (const item of items) { + const entityPresentation = (await entityPresentationApi.forEntity(item) + ?.promise) as EntityRefPresentationSnapshot[]; + entityPresentation.map(e => primaryTitles.push(e.primaryTitle)); + } + + return { items, primaryTitles }; }); const allowArbitraryValues = uiSchema['ui:options']?.allowArbitraryValues ?? true; - const getLabel = useCallback( - (ref: string) => { - try { - return humanizeEntityRef( - parseEntityRef(ref, { defaultKind, defaultNamespace }), - { - defaultKind, - defaultNamespace, - }, - ); - } catch (err) { - return ref; - } - }, - [defaultKind, defaultNamespace], - ); - const onSelect = useCallback( (_: any, refs: (string | Entity)[], reason: AutocompleteChangeReason) => { const values = refs .map(ref => { if (typeof ref !== 'string') { // if ref does not exist: pass 'undefined' to trigger validation for required value - return ref ? stringifyEntityRef(ref as Entity) : undefined; + return ref + ? entityPresentationApi.forEntity(ref, { + defaultKind, + defaultNamespace, + }).snapshot.entityRef + : undefined; } if (reason === 'blur' || reason === 'create-option') { // Add in default namespace, etc. @@ -126,14 +123,21 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => { onChange(values); }, - [onChange, formData, defaultKind, defaultNamespace, allowArbitraryValues], + [ + onChange, + formData, + defaultKind, + defaultNamespace, + allowArbitraryValues, + entityPresentationApi, + ], ); useEffect(() => { - if (entities?.length === 1) { - onChange([stringifyEntityRef(entities[0])]); + if (entities?.items?.length === 1) { + onChange([stringifyEntityRef(entities.items[0])]); } - }, [entities, onChange]); + }, [entities?.items, onChange]); return ( { formData && formData.includes(stringifyEntityRef(e)), - ) ?? (allowArbitraryValues && formData ? formData.map(getLabel) : []) + ) ?? + (allowArbitraryValues && formData + ? entities?.primaryTitles || [] + : []) } loading={loading} onChange={onSelect} - options={entities || []} + options={entities?.items || []} + renderOption={option => } getOptionLabel={option => // option can be a string due to freeSolo. typeof option === 'string' ? option - : humanizeEntityRef(option, { defaultKind, defaultNamespace })! + : entityPresentationApi.forEntity(option, { + defaultKind, + defaultNamespace, + }).snapshot.entityRef! } autoSelect freeSolo={allowArbitraryValues} @@ -170,7 +181,10 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => { label={title} margin="dense" helperText={description} - FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 } }} + FormHelperTextProps={{ + margin: 'dense', + style: { marginLeft: 0 }, + }} variant="outlined" required={required} InputProps={params.InputProps} From 82214e72c9a8abc6c39613841fc48f30210e39eb Mon Sep 17 00:00:00 2001 From: NIKUNJ LALITKUMAR HUDKA Date: Sat, 20 Apr 2024 14:18:23 -0300 Subject: [PATCH 024/251] Revert "bugfix: Proper error thrown in plugin-azure-devops-backend when gitRepository is not found." This reverts commit 88191bb54ebf9e118f3c5fbbd2a7ab7b1e669bd6. Signed-off-by: NIKUNJ LALITKUMAR HUDKA --- .changeset/three-sheep-remember.md | 5 --- .../src/api/AzureDevOpsApi.test.ts | 37 ------------------- .../src/api/AzureDevOpsApi.ts | 20 ---------- 3 files changed, 62 deletions(-) delete mode 100644 .changeset/three-sheep-remember.md diff --git a/.changeset/three-sheep-remember.md b/.changeset/three-sheep-remember.md deleted file mode 100644 index 51ffc8e3db..0000000000 --- a/.changeset/three-sheep-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-azure-devops-backend': minor ---- - -Fixed bug in plugin-azure-devops-backend where proper error was not thrown when gitRepository was not found. diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts index a056beee02..174c5ff6cd 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts @@ -465,43 +465,6 @@ describe('AzureDevOpsApi', () => { ]); }); - it('should throw error when gitRepository is undefined', async () => { - const mockApi = { - getGitApi: jest.fn().mockReturnValue({}), - serverUrl: 'serverUrl', - }; - - (WebApi as unknown as jest.Mock).mockImplementation(() => mockApi); - - const api = AzureDevOpsApi.fromConfig(mockConfig, { - logger: mockLogger, - urlReader: mockUrlReader, - }); - - const pullRequestOptions: PullRequestOptions = { - top: 10, - status: PullRequestStatus.Active, - }; - - api.getGitRepository = jest.fn().mockResolvedValue(undefined); - - const temp = async () => { - try { - await api.getPullRequests('project', 'repo', pullRequestOptions); - return null; - } catch (error) { - return error; - } - }; - - const error = await temp(); - - expect(error).toHaveProperty( - 'message', - 'No repository found for Project "project" with Repository "repo" on host "undefined" under organization "undefined".', - ); - }); - it('should get build definitions', async () => { const mockBuilds: Build[] = [ { diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index 468d6a9d88..4ed54e63eb 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -231,11 +231,6 @@ export class AzureDevOpsApi { host, org, ); - if (!gitRepository) { - throw new Error( - `No repository found for Project "${projectName}" with Repository "${repoName}" on host "${host}" under organization "${org}".`, - ); - } const buildList = await this.getBuildList( projectName, gitRepository.id as string, @@ -267,11 +262,6 @@ export class AzureDevOpsApi { host, org, ); - if (!gitRepository) { - throw new Error( - `No repository found for Project "${projectName}" with Repository "${repoName}" on host "${host}" under organization "${org}".`, - ); - } const webApi = await this.getWebApi(host, org); const client = await webApi.getGitApi(); const tagRefs: GitRef[] = await client.getRefs( @@ -314,11 +304,6 @@ export class AzureDevOpsApi { host, org, ); - if (!gitRepository) { - throw new Error( - `No repository found for Project "${projectName}" with Repository "${repoName}" on host "${host}" under organization "${org}".`, - ); - } const webApi = await this.getWebApi(host, org); const client = await webApi.getGitApi(); const searchCriteria: GitPullRequestSearchCriteria = { @@ -529,11 +514,6 @@ export class AzureDevOpsApi { host, org, ); - if (!gitRepository) { - throw new Error( - `No repository found for Project "${projectName}" with Repository "${repoName}" on host "${host}" under organization "${org}".`, - ); - } repoId = gitRepository.id; } From e1174b01b20e4a29fc81b07f25798aebb2595729 Mon Sep 17 00:00:00 2001 From: NIKUNJ LALITKUMAR HUDKA Date: Sat, 20 Apr 2024 15:28:46 -0300 Subject: [PATCH 025/251] chore: changeset added for this feature Signed-off-by: NIKUNJ LALITKUMAR HUDKA --- .changeset/five-cows-crash.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/five-cows-crash.md diff --git a/.changeset/five-cows-crash.md b/.changeset/five-cows-crash.md new file mode 100644 index 0000000000..2c298dbad3 --- /dev/null +++ b/.changeset/five-cows-crash.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-import': minor +--- + +`EntityListComponent` uses `entityPresentationApi` instead of `humanizeEntityRef` to display Entity From 4946ed88513e70ed5787c4503096810097f994c3 Mon Sep 17 00:00:00 2001 From: NIKUNJ LALITKUMAR HUDKA Date: Sat, 20 Apr 2024 15:45:50 -0300 Subject: [PATCH 026/251] chore: yarn.lock updated with new dependencies Signed-off-by: NIKUNJ LALITKUMAR HUDKA --- yarn.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn.lock b/yarn.lock index 0a27eecdea..5b5aef8874 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5868,6 +5868,7 @@ __metadata: "@backstage/frontend-plugin-api": "workspace:^" "@backstage/integration": "workspace:^" "@backstage/integration-react": "workspace:^" + "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" "@backstage/test-utils": "workspace:^" From 4268696352ec81671b543059ce732b1442b6ee92 Mon Sep 17 00:00:00 2001 From: NIKUNJ LALITKUMAR HUDKA Date: Sun, 21 Apr 2024 21:57:36 -0300 Subject: [PATCH 027/251] chore: changeset added Signed-off-by: NIKUNJ LALITKUMAR HUDKA --- .changeset/olive-rockets-drum.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/olive-rockets-drum.md diff --git a/.changeset/olive-rockets-drum.md b/.changeset/olive-rockets-drum.md new file mode 100644 index 0000000000..12f80dab5c --- /dev/null +++ b/.changeset/olive-rockets-drum.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-catalog-react': minor +'@backstage/plugin-scaffolder': minor +'@backstage/plugin-catalog': minor +--- + +`MultiEntityPicker` uses `entityPresentationApi` instead of `humanizeEntityRef` to display entity. Also, `EntityPresentationApi` now allows `promise` getter for under asynchronous process of presentation api From ac6cff672e9dd1e8194a342610e77f4a2d3eeec1 Mon Sep 17 00:00:00 2001 From: NIKUNJ LALITKUMAR HUDKA Date: Mon, 22 Apr 2024 22:23:01 -0300 Subject: [PATCH 028/251] testcase updated for new promise getter in api Signed-off-by: NIKUNJ LALITKUMAR HUDKA --- .../DefaultEntityPresentationApi.test.ts | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts index 42ff777cbd..475bfe1660 100644 --- a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts +++ b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts @@ -35,6 +35,7 @@ describe('DefaultEntityPresentationApi', () => { Icon: expect.anything(), }, update$: undefined, + promise: new Promise((resolve, reject) => resolve({})), }); expect( @@ -48,6 +49,7 @@ describe('DefaultEntityPresentationApi', () => { Icon: expect.anything(), }, update$: undefined, + promise: new Promise((resolve, reject) => resolve({})), }); expect( @@ -63,6 +65,7 @@ describe('DefaultEntityPresentationApi', () => { Icon: expect.anything(), }, update$: undefined, + promise: new Promise((resolve, reject) => resolve({})), }); const entity: Entity = { @@ -85,6 +88,7 @@ describe('DefaultEntityPresentationApi', () => { Icon: expect.anything(), }, update$: undefined, + promise: new Promise((resolve, reject) => resolve({})), }); }); @@ -150,6 +154,47 @@ describe('DefaultEntityPresentationApi', () => { }), ); }); + + it('returns the correct snapshots via promise', async () => { + const catalogApi = { + getEntitiesByRefs: jest.fn(), + }; + const api = DefaultEntityPresentationApi.create({ + catalogApi: catalogApi as Partial as any, + }); + + catalogApi.getEntitiesByRefs.mockResolvedValueOnce({ + items: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + namespace: 'default', + etag: 'something', + }, + spec: { + type: 'service', + }, + }, + ], + }); + + const entityRef = 'component:default/test'; + const entitySnapshot = { + entityRef: entityRef, + primaryTitle: 'test', + secondaryTitle: 'component:default/test | service', + Icon: expect.anything(), + }; + + const promise = api.forEntity(entityRef).promise; + + const snapshots = await promise; + + expect(snapshots.length).toEqual(1); // Only one snapshot expected + expect(snapshots[0]).toEqual(entitySnapshot); // Snapshot should match the simulated one + }); }); async function consumePresentation( From 30e92e77097540746d66d2d3b0330e8cf30119d9 Mon Sep 17 00:00:00 2001 From: NIKUNJ LALITKUMAR HUDKA Date: Mon, 22 Apr 2024 23:18:55 -0300 Subject: [PATCH 029/251] chore: lint errors resolved Signed-off-by: NIKUNJ LALITKUMAR HUDKA --- .../EntityPresentationApi.ts | 2 +- .../DefaultEntityPresentationApi.test.ts | 12 ++--- .../DefaultEntityPresentationApi.ts | 46 +++++++++---------- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts index de5449917a..f4f1193718 100644 --- a/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts @@ -113,7 +113,7 @@ export interface EntityRefPresentation { /* The `promise` property in the `EntityRefPresentation` interface is defining a property named `promise` that holds a promise. This promise resolves to an array of `EntityRefPresentationSnapshot` objects. */ - promise: Promise; + promise?: Promise; } /** diff --git a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts index 475bfe1660..c37efb204f 100644 --- a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts +++ b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts @@ -35,7 +35,7 @@ describe('DefaultEntityPresentationApi', () => { Icon: expect.anything(), }, update$: undefined, - promise: new Promise((resolve, reject) => resolve({})), + promise: new Promise(resolve => resolve({})), }); expect( @@ -49,7 +49,7 @@ describe('DefaultEntityPresentationApi', () => { Icon: expect.anything(), }, update$: undefined, - promise: new Promise((resolve, reject) => resolve({})), + promise: new Promise(resolve => resolve({})), }); expect( @@ -65,7 +65,7 @@ describe('DefaultEntityPresentationApi', () => { Icon: expect.anything(), }, update$: undefined, - promise: new Promise((resolve, reject) => resolve({})), + promise: new Promise(resolve => resolve({})), }); const entity: Entity = { @@ -88,7 +88,7 @@ describe('DefaultEntityPresentationApi', () => { Icon: expect.anything(), }, update$: undefined, - promise: new Promise((resolve, reject) => resolve({})), + promise: new Promise(resolve => resolve({})), }); }); @@ -192,8 +192,8 @@ describe('DefaultEntityPresentationApi', () => { const snapshots = await promise; - expect(snapshots.length).toEqual(1); // Only one snapshot expected - expect(snapshots[0]).toEqual(entitySnapshot); // Snapshot should match the simulated one + expect(snapshots?.length).toEqual(1); // Only one snapshot expected + expect(snapshots?.[0]).toEqual(entitySnapshot); // Snapshot should match the simulated one }); }); diff --git a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts index bbd5540d15..c7572cca3b 100644 --- a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts +++ b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts @@ -302,30 +302,28 @@ export class DefaultEntityPresentationApi implements EntityPresentationApi { snapshot: initialSnapshot, update$: observable, get promise() { - return new Promise( - (resolve, reject) => { - if (!observable) { - resolve([initialSnapshot]); - } else { - const res: EntityRefPresentationSnapshot[] = []; - const subscription = observable.subscribe({ - next: snapshot => { - res.push(snapshot); - }, - error: error => { - initialSnapshot = { - primaryTitle: entityRef, - entityRef: entityRef, - }; - }, - complete() { - subscription.unsubscribe(); - resolve(res); - }, - }); - } - }, - ); + return new Promise(resolve => { + if (!observable) { + resolve([initialSnapshot]); + } else { + const res: EntityRefPresentationSnapshot[] = []; + const subscription = observable.subscribe({ + next: snapshot => { + res.push(snapshot); + }, + error: () => { + initialSnapshot = { + primaryTitle: entityRef, + entityRef: entityRef, + }; + }, + complete() { + subscription.unsubscribe(); + resolve(res); + }, + }); + } + }); }, }; return entityRefPresentation; From 96cd13eca2ddc488a35f9bbb11ae7fdba29c22bf Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Tue, 23 Apr 2024 12:36:38 +0200 Subject: [PATCH 030/251] feat: add property ownerPickerMode to TechDocsIndexPage and DefaultApiExplorerPage Signed-off-by: Benjamin Janssens --- .changeset/gentle-baboons-peel.md | 5 +++++ .changeset/tiny-pandas-return.md | 5 +++++ plugins/api-docs/api-report.md | 2 ++ .../ApiExplorerPage/DefaultApiExplorerPage.tsx | 11 +++++++++-- plugins/techdocs/api-report.md | 2 ++ .../src/home/components/DefaultTechDocsHome.tsx | 4 ++-- .../src/home/components/TechDocsIndexPage.tsx | 6 +++++- 7 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 .changeset/gentle-baboons-peel.md create mode 100644 .changeset/tiny-pandas-return.md diff --git a/.changeset/gentle-baboons-peel.md b/.changeset/gentle-baboons-peel.md new file mode 100644 index 0000000000..d63396860b --- /dev/null +++ b/.changeset/gentle-baboons-peel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': minor +--- + +Added property ownerPickerMode to TechDocsIndexPage diff --git a/.changeset/tiny-pandas-return.md b/.changeset/tiny-pandas-return.md new file mode 100644 index 0000000000..d584accccf --- /dev/null +++ b/.changeset/tiny-pandas-return.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Added property ownerPickerMode to DefaultApiExplorerPage diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md index 7523198298..2fc852777b 100644 --- a/plugins/api-docs/api-report.md +++ b/plugins/api-docs/api-report.md @@ -9,6 +9,7 @@ import { ApiEntity } from '@backstage/catalog-model'; import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { CatalogTableRow } from '@backstage/plugin-catalog'; +import { EntityOwnerPickerProps } from '@backstage/plugin-catalog-react'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { InfoCardVariants } from '@backstage/core-components'; import { JSX as JSX_2 } from 'react'; @@ -105,6 +106,7 @@ export type DefaultApiExplorerPageProps = { initiallySelectedFilter?: UserListFilterKind; columns?: TableColumn[]; actions?: TableProps['actions']; + ownerPickerMode?: EntityOwnerPickerProps['mode']; }; // @public (undocumented) diff --git a/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.tsx b/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.tsx index dc83c2528d..6b5917c52d 100644 --- a/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.tsx +++ b/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.tsx @@ -35,6 +35,7 @@ import { UserListFilterKind, UserListPicker, CatalogFilterLayout, + EntityOwnerPickerProps, } from '@backstage/plugin-catalog-react'; import React from 'react'; import { registerComponentRouteRef } from '../../routes'; @@ -60,6 +61,7 @@ export type DefaultApiExplorerPageProps = { initiallySelectedFilter?: UserListFilterKind; columns?: TableColumn[]; actions?: TableProps['actions']; + ownerPickerMode?: EntityOwnerPickerProps['mode']; }; /** @@ -67,7 +69,12 @@ export type DefaultApiExplorerPageProps = { * @public */ export const DefaultApiExplorerPage = (props: DefaultApiExplorerPageProps) => { - const { initiallySelectedFilter = 'all', columns, actions } = props; + const { + initiallySelectedFilter = 'all', + columns, + actions, + ownerPickerMode, + } = props; const configApi = useApi(configApiRef); const generatedSubtitle = `${ @@ -101,7 +108,7 @@ export const DefaultApiExplorerPage = (props: DefaultApiExplorerPageProps) => {