Merge remote-tracking branch 'origin/master' into shutdown-tasks-on-restart
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
'@backstage/plugin-catalog-node': minor
|
||||
---
|
||||
|
||||
Support adding location analyzers in new catalog analysis extension point and move `AnalyzeOptions` and `ScmLocationAnalyzer` types to `@backstage/plugin-catalog-node`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-home': patch
|
||||
---
|
||||
|
||||
Fix bug where `retrieveAll` method wasn't fetching visits
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend-module-microsoft-provider': patch
|
||||
---
|
||||
|
||||
Added support for specifying a domain hint on the Microsoft authentication provider configuration.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-github': patch
|
||||
---
|
||||
|
||||
Import `AnalyzeOptions` and `ScmLocationAnalyzer` types from `@backstage/plugin-catalog-node`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-search-react': patch
|
||||
---
|
||||
|
||||
Minor refactor of search bar analytics capture
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
# Fetch the commit that's merged into the base rather than the target ref
|
||||
# This will let us diff only the contents of the PR, without fetching more history
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
ref: '${{ github.event.pull_request.merge_commit_sha }}'
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
@@ -67,10 +67,10 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
@@ -196,12 +196,12 @@ jobs:
|
||||
INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- name: fetch branch master
|
||||
run: git fetch origin master
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -19,13 +19,13 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
path: backstage
|
||||
ref: v${{ github.event.client_payload.version }}
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -28,10 +28,10 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: use node.js 18.x
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: 18.x
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -19,10 +19,10 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: use node.js 18.x
|
||||
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2
|
||||
with:
|
||||
node-version: 18.x
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -60,10 +60,10 @@ jobs:
|
||||
INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
@@ -142,10 +142,10 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -14,13 +14,13 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
# Fetch changes to previous commit - required for 'only_changed' in Prettier action
|
||||
fetch-depth: 0
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
fetch-depth: 2
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
# Setup node & install deps before checkout, keeping install quick
|
||||
- uses: actions/setup-node@v3.8.1
|
||||
- uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: 18.x
|
||||
- name: Install dependencies
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
run: npm install semver@7.3.5 fs-extra@10.0.0 @manypkg/get-packages@1.1.1
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
path: backstage
|
||||
# 'v' prefix is added here for the tag, we keep it out of the manifest logic
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
# Checkout backstage/versions into /backstage/versions, which is where store the output
|
||||
- name: Checkout versions
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
repository: backstage/versions
|
||||
path: backstage/versions
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
fetch-depth: 2
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
@@ -16,10 +16,10 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: use node.js 18.x
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: 18.x
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- name: Monitor and Synchronize Snyk Policies
|
||||
uses: snyk/actions/node@39091e69b560da335383b404e50d65b408f4f812 # master
|
||||
with:
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
fetch-depth: 20000
|
||||
fetch-tags: true
|
||||
|
||||
@@ -25,10 +25,10 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: setup-node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: 18.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout git repo
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Render Compose File
|
||||
run: |
|
||||
BACKSTAGE_IMAGE=$(echo ${{ needs.build-backstage.outputs.tags }})
|
||||
|
||||
@@ -24,9 +24,9 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: 18.x
|
||||
- name: yarn install
|
||||
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
# Vale does not support file excludes, so we use the script to generate a list of files instead
|
||||
# The action also does not allow args or a local config file to be passed in, so the files array
|
||||
|
||||
@@ -26,10 +26,10 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
git config --global user.name 'GitHub e2e user'
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -34,8 +34,8 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/setup-python@v4.7.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: '3.9'
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
@@ -50,13 +50,13 @@ jobs:
|
||||
git config --global user.name 'GitHub e2e user'
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
- name: setup python
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Install Fossa
|
||||
run: "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash"
|
||||
|
||||
@@ -28,10 +28,10 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: use node.js 18.x
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
|
||||
@@ -32,12 +32,12 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
fetch-depth: 0 # Required to retrieve git history
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -33,10 +33,10 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: use node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v3.8.2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
@@ -38,13 +38,18 @@ auth:
|
||||
clientId: ${AUTH_MICROSOFT_CLIENT_ID}
|
||||
clientSecret: ${AUTH_MICROSOFT_CLIENT_SECRET}
|
||||
tenantId: ${AUTH_MICROSOFT_TENANT_ID}
|
||||
domainHint: ${AZURE_TENANT_ID}
|
||||
```
|
||||
|
||||
The Microsoft provider is a structure with three configuration keys:
|
||||
The Microsoft provider is a structure with three mandatory configuration keys:
|
||||
|
||||
- `clientId`: Application (client) ID, found on App Registration > Overview
|
||||
- `clientSecret`: Secret, found on App Registration > Certificates & secrets
|
||||
- `tenantId`: Directory (tenant) ID, found on App Registration > Overview
|
||||
- `domainHint` (optional): Typically the same as `tenantId`.
|
||||
Leave blank if your app registration is multi tenant.
|
||||
When specified, this reduces login friction for users with accounts in multiple tenants by automatically filtering away accounts from other tenants.
|
||||
For more details, see [Home Realm Discovery](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/home-realm-discovery-policy)
|
||||
|
||||
## Outbound Network Access
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ title: Migration to Yarn 3
|
||||
description: Guide for how to migrate a Backstage project to use Yarn 3
|
||||
---
|
||||
|
||||
> NOTE: We do not yet recommend all projects to migrate to Yarn 3. Only do so if you have specific reasons for it.
|
||||
|
||||
While Backstage projects created with `@backstage/create-app` use [Yarn 1](https://classic.yarnpkg.com/) by default, it
|
||||
is possible to switch them to instead use [Yarn 3](https://yarnpkg.com/). Tools like `yarn backstage-cli versions:bump` will
|
||||
still work, as they recognize both lockfile formats.
|
||||
|
||||
@@ -24,8 +24,8 @@ export class CommonJSModuleLoader implements ModuleLoader {
|
||||
backstageRoot: string,
|
||||
dynamicPluginsPaths: string[],
|
||||
): Promise<void> {
|
||||
const allowedNodeModulesPaths = [
|
||||
`${backstageRoot}/node_modules`,
|
||||
const backstageRootNodeModulesPath = `${backstageRoot}/node_modules`;
|
||||
const dynamicNodeModulesPaths = [
|
||||
...dynamicPluginsPaths.map(p => path.resolve(p, 'node_modules')),
|
||||
];
|
||||
const Module = require('module');
|
||||
@@ -35,8 +35,12 @@ export class CommonJSModuleLoader implements ModuleLoader {
|
||||
if (!dynamicPluginsPaths.some(p => from.startsWith(p))) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const filtered = result.filter(p => allowedNodeModulesPaths.includes(p));
|
||||
const filtered = result.filter(nodeModulePath => {
|
||||
return (
|
||||
nodeModulePath === backstageRootNodeModulesPath ||
|
||||
dynamicNodeModulesPaths.some(p => nodeModulePath.startsWith(p))
|
||||
);
|
||||
});
|
||||
this.logger.debug(
|
||||
`Overriding node_modules search path for dynamic plugin ${from} to: ${filtered}`,
|
||||
);
|
||||
|
||||
@@ -15,7 +15,10 @@ export const authModuleMicrosoftProvider: () => BackendFeature;
|
||||
|
||||
// @public (undocumented)
|
||||
export const microsoftAuthenticator: OAuthAuthenticator<
|
||||
PassportOAuthAuthenticatorHelper,
|
||||
{
|
||||
helper: PassportOAuthAuthenticatorHelper;
|
||||
domainHint: string | undefined;
|
||||
},
|
||||
PassportProfile
|
||||
>;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface Config {
|
||||
* @visibility secret
|
||||
*/
|
||||
clientSecret: string;
|
||||
domainHint?: string;
|
||||
callbackUrl?: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -30,8 +30,9 @@ export const microsoftAuthenticator = createOAuthAuthenticator({
|
||||
const clientId = config.getString('clientId');
|
||||
const clientSecret = config.getString('clientSecret');
|
||||
const tenantId = config.getString('tenantId');
|
||||
const domainHint = config.getOptionalString('domainHint');
|
||||
|
||||
return PassportOAuthAuthenticatorHelper.from(
|
||||
const helper = PassportOAuthAuthenticatorHelper.from(
|
||||
new MicrosoftStrategy(
|
||||
{
|
||||
clientID: clientId,
|
||||
@@ -55,19 +56,30 @@ export const microsoftAuthenticator = createOAuthAuthenticator({
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return {
|
||||
helper,
|
||||
domainHint,
|
||||
};
|
||||
},
|
||||
|
||||
async start(input, helper) {
|
||||
return helper.start(input, {
|
||||
async start(input, ctx) {
|
||||
const options: Record<string, string> = {
|
||||
accessType: 'offline',
|
||||
});
|
||||
};
|
||||
|
||||
if (ctx.domainHint !== undefined) {
|
||||
options.domain_hint = ctx.domainHint;
|
||||
}
|
||||
|
||||
return ctx.helper.start(input, options);
|
||||
},
|
||||
|
||||
async authenticate(input, helper) {
|
||||
return helper.authenticate(input);
|
||||
async authenticate(input, ctx) {
|
||||
return ctx.helper.authenticate(input);
|
||||
},
|
||||
|
||||
async refresh(input, helper) {
|
||||
return helper.refresh(input);
|
||||
async refresh(input, ctx) {
|
||||
return ctx.helper.refresh(input);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ import request from 'supertest';
|
||||
import { authModuleMicrosoftProvider } from './module';
|
||||
|
||||
describe('authModuleMicrosoftProvider', () => {
|
||||
it('should start', async () => {
|
||||
it('should start without domain hint', async () => {
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
authPlugin,
|
||||
@@ -77,4 +77,63 @@ describe('authModuleMicrosoftProvider', () => {
|
||||
nonce: decodeURIComponent(nonceCookie.value),
|
||||
});
|
||||
});
|
||||
|
||||
it('should start with domain hint', async () => {
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
authPlugin,
|
||||
authModuleMicrosoftProvider,
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
app: {
|
||||
baseUrl: 'http://localhost:3000',
|
||||
},
|
||||
auth: {
|
||||
providers: {
|
||||
microsoft: {
|
||||
development: {
|
||||
clientId: 'another-client-id',
|
||||
clientSecret: 'another-client-secret',
|
||||
tenantId: 'another-tenant-id',
|
||||
domainHint: 'somedomain',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const agent = request.agent(server);
|
||||
|
||||
const res = await agent.get('/api/auth/microsoft/start?env=development');
|
||||
|
||||
expect(res.status).toEqual(302);
|
||||
|
||||
const nonceCookie = agent.jar.getCookie('microsoft-nonce', {
|
||||
domain: 'localhost',
|
||||
path: '/api/auth/microsoft/handler',
|
||||
script: false,
|
||||
secure: false,
|
||||
});
|
||||
expect(nonceCookie).toBeDefined();
|
||||
|
||||
const startUrl = new URL(res.get('location'));
|
||||
expect(startUrl.origin).toBe('https://login.microsoftonline.com');
|
||||
expect(startUrl.pathname).toBe('/another-tenant-id/oauth2/v2.0/authorize');
|
||||
expect(Object.fromEntries(startUrl.searchParams)).toEqual({
|
||||
response_type: 'code',
|
||||
scope: 'user.read',
|
||||
client_id: 'another-client-id',
|
||||
redirect_uri: `http://localhost:${server.port()}/api/auth/microsoft/handler/frame`,
|
||||
state: expect.any(String),
|
||||
domain_hint: 'somedomain',
|
||||
});
|
||||
|
||||
expect(decodeOAuthState(startUrl.searchParams.get('state')!)).toEqual({
|
||||
env: 'development',
|
||||
nonce: decodeURIComponent(nonceCookie.value),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AnalyzeOptions } from '@backstage/plugin-catalog-backend';
|
||||
import { AnalyzeOptions } from '@backstage/plugin-catalog-node';
|
||||
import { CatalogProcessor } from '@backstage/plugin-catalog-node';
|
||||
import { CatalogProcessorEmit } from '@backstage/plugin-catalog-node';
|
||||
import { Config } from '@backstage/config';
|
||||
@@ -21,7 +21,7 @@ import { Logger } from 'winston';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { PluginTaskScheduler } from '@backstage/backend-tasks';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-backend';
|
||||
import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-node';
|
||||
import { TaskRunner } from '@backstage/backend-tasks';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import { UserEntity } from '@backstage/catalog-model';
|
||||
|
||||
@@ -27,7 +27,7 @@ import parseGitUrl from 'git-url-parse';
|
||||
import {
|
||||
AnalyzeOptions,
|
||||
ScmLocationAnalyzer,
|
||||
} from '@backstage/plugin-catalog-backend';
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import {
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
|
||||
@@ -10,6 +10,7 @@ import { AnalyzeLocationExistingEntity as AnalyzeLocationExistingEntity_2 } from
|
||||
import { AnalyzeLocationGenerateEntity as AnalyzeLocationGenerateEntity_2 } from '@backstage/plugin-catalog-common';
|
||||
import { AnalyzeLocationRequest as AnalyzeLocationRequest_2 } from '@backstage/plugin-catalog-common';
|
||||
import { AnalyzeLocationResponse as AnalyzeLocationResponse_2 } from '@backstage/plugin-catalog-common';
|
||||
import { AnalyzeOptions as AnalyzeOptions_2 } from '@backstage/plugin-catalog-node';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import type { CatalogCollatorEntityTransformer as CatalogCollatorEntityTransformer_2 } from '@backstage/plugin-search-backend-module-catalog';
|
||||
import { CatalogEntityDocument } from '@backstage/plugin-catalog-common';
|
||||
@@ -50,6 +51,7 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { PluginTaskScheduler } from '@backstage/backend-tasks';
|
||||
import { Router } from 'express';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { ScmLocationAnalyzer as ScmLocationAnalyzer_2 } from '@backstage/plugin-catalog-node';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
import { Validators } from '@backstage/catalog-model';
|
||||
@@ -69,11 +71,8 @@ export type AnalyzeLocationRequest = AnalyzeLocationRequest_2;
|
||||
// @public @deprecated (undocumented)
|
||||
export type AnalyzeLocationResponse = AnalyzeLocationResponse_2;
|
||||
|
||||
// @public (undocumented)
|
||||
export type AnalyzeOptions = {
|
||||
url: string;
|
||||
catalogFilename?: string;
|
||||
};
|
||||
// @public @deprecated (undocumented)
|
||||
export type AnalyzeOptions = AnalyzeOptions_2;
|
||||
|
||||
// @public (undocumented)
|
||||
export class AnnotateLocationEntityProcessor implements CatalogProcessor_2 {
|
||||
@@ -134,7 +133,7 @@ export class CatalogBuilder {
|
||||
...providers: Array<EntityProvider_2 | Array<EntityProvider_2>>
|
||||
): CatalogBuilder;
|
||||
addLocationAnalyzers(
|
||||
...analyzers: Array<ScmLocationAnalyzer | Array<ScmLocationAnalyzer>>
|
||||
...analyzers: Array<ScmLocationAnalyzer_2 | Array<ScmLocationAnalyzer_2>>
|
||||
): CatalogBuilder;
|
||||
addPermissionRules(
|
||||
...permissionRules: Array<
|
||||
@@ -468,13 +467,8 @@ export const processingResult: Readonly<{
|
||||
readonly refresh: (key: string) => CatalogProcessorResult_2;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ScmLocationAnalyzer = {
|
||||
supports(url: string): boolean;
|
||||
analyze(options: AnalyzeOptions): Promise<{
|
||||
existing: AnalyzeLocationExistingEntity[];
|
||||
}>;
|
||||
};
|
||||
// @public @deprecated (undocumented)
|
||||
export type ScmLocationAnalyzer = ScmLocationAnalyzer_2;
|
||||
|
||||
// @public (undocumented)
|
||||
export class UrlReaderProcessor implements CatalogProcessor_2 {
|
||||
|
||||
@@ -33,6 +33,8 @@ import {
|
||||
type EntityProvider as _EntityProvider,
|
||||
type EntityProviderConnection as _EntityProviderConnection,
|
||||
type EntityProviderMutation as _EntityProviderMutation,
|
||||
type AnalyzeOptions as _AnalyzeOptions,
|
||||
type ScmLocationAnalyzer as _ScmLocationAnalyzer,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import { type LocationSpec as _LocationSpec } from '@backstage/plugin-catalog-common';
|
||||
|
||||
@@ -141,3 +143,13 @@ export type EntityProviderMutation = _EntityProviderMutation;
|
||||
* @deprecated use the same type from `@backstage/plugin-catalog-common` instead
|
||||
*/
|
||||
export type LocationSpec = _LocationSpec;
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-catalog-node` instead
|
||||
*/
|
||||
export type AnalyzeOptions = _AnalyzeOptions;
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-catalog-node` instead
|
||||
*/
|
||||
export type ScmLocationAnalyzer = _ScmLocationAnalyzer;
|
||||
|
||||
@@ -18,11 +18,12 @@ import { Logger } from 'winston';
|
||||
import parseGitUrl from 'git-url-parse';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { LocationAnalyzer, ScmLocationAnalyzer } from './types';
|
||||
import { LocationAnalyzer } from './types';
|
||||
import {
|
||||
AnalyzeLocationRequest,
|
||||
AnalyzeLocationResponse,
|
||||
} from '@backstage/plugin-catalog-common';
|
||||
import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-node';
|
||||
|
||||
export class RepoLocationAnalyzer implements LocationAnalyzer {
|
||||
private readonly logger: Logger;
|
||||
|
||||
@@ -21,6 +21,4 @@ export type {
|
||||
AnalyzeLocationRequest,
|
||||
AnalyzeLocationResponse,
|
||||
LocationAnalyzer,
|
||||
ScmLocationAnalyzer,
|
||||
AnalyzeOptions,
|
||||
} from './types';
|
||||
|
||||
@@ -78,19 +78,3 @@ export type LocationAnalyzer = {
|
||||
location: AnalyzeLocationRequest,
|
||||
): Promise<AnalyzeLocationResponse>;
|
||||
};
|
||||
/** @public */
|
||||
export type AnalyzeOptions = {
|
||||
url: string;
|
||||
catalogFilename?: string;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type ScmLocationAnalyzer = {
|
||||
/** The method that decides if this analyzer can work with the provided url */
|
||||
supports(url: string): boolean;
|
||||
/** This function can return an array of already existing entities */
|
||||
analyze(options: AnalyzeOptions): Promise<{
|
||||
/** Existing entities in the analyzed location */
|
||||
existing: AnalyzeLocationExistingEntity[];
|
||||
}>;
|
||||
};
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
CatalogProcessor,
|
||||
CatalogProcessorParser,
|
||||
EntityProvider,
|
||||
ScmLocationAnalyzer,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import {
|
||||
AnnotateLocationEntityProcessor,
|
||||
@@ -57,7 +58,7 @@ import {
|
||||
yamlPlaceholderResolver,
|
||||
} from '../modules/core/PlaceholderProcessor';
|
||||
import { defaultEntityDataParser } from '../modules/util/parse';
|
||||
import { LocationAnalyzer, ScmLocationAnalyzer } from '../ingestion/types';
|
||||
import { LocationAnalyzer } from '../ingestion/types';
|
||||
import { CatalogProcessingEngine } from '../processing';
|
||||
import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase';
|
||||
import { applyDatabaseMigrations } from '../database/migrations';
|
||||
|
||||
@@ -19,17 +19,22 @@ import {
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { CatalogBuilder } from './CatalogBuilder';
|
||||
import {
|
||||
CatalogAnalysisExtensionPoint,
|
||||
catalogAnalysisExtensionPoint,
|
||||
CatalogProcessingExtensionPoint,
|
||||
catalogProcessingExtensionPoint,
|
||||
} from '@backstage/plugin-catalog-node/alpha';
|
||||
import {
|
||||
CatalogProcessor,
|
||||
EntityProvider,
|
||||
ScmLocationAnalyzer,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import { loggerToWinstonLogger } from '@backstage/backend-common';
|
||||
import { PlaceholderResolver } from '../modules';
|
||||
|
||||
class CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint {
|
||||
class CatalogProcessingExtensionPointImpl
|
||||
implements CatalogProcessingExtensionPoint
|
||||
{
|
||||
#processors = new Array<CatalogProcessor>();
|
||||
#entityProviders = new Array<EntityProvider>();
|
||||
#placeholderResolvers: Record<string, PlaceholderResolver> = {};
|
||||
@@ -67,6 +72,20 @@ class CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint {
|
||||
}
|
||||
}
|
||||
|
||||
class CatalogAnalysisExtensionPointImpl
|
||||
implements CatalogAnalysisExtensionPoint
|
||||
{
|
||||
#locationAnalyzers = new Array<ScmLocationAnalyzer>();
|
||||
|
||||
addLocationAnalyzer(analyzer: ScmLocationAnalyzer): void {
|
||||
this.#locationAnalyzers.push(analyzer);
|
||||
}
|
||||
|
||||
get locationAnalyzers() {
|
||||
return this.#locationAnalyzers;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Catalog plugin
|
||||
* @alpha
|
||||
@@ -74,13 +93,19 @@ class CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint {
|
||||
export const catalogPlugin = createBackendPlugin({
|
||||
pluginId: 'catalog',
|
||||
register(env) {
|
||||
const processingExtensions = new CatalogExtensionPointImpl();
|
||||
const processingExtensions = new CatalogProcessingExtensionPointImpl();
|
||||
// plugins depending on this API will be initialized before this plugins init method is executed.
|
||||
env.registerExtensionPoint(
|
||||
catalogProcessingExtensionPoint,
|
||||
processingExtensions,
|
||||
);
|
||||
|
||||
const analysisExtensions = new CatalogAnalysisExtensionPointImpl();
|
||||
env.registerExtensionPoint(
|
||||
catalogAnalysisExtensionPoint,
|
||||
analysisExtensions,
|
||||
);
|
||||
|
||||
env.registerInit({
|
||||
deps: {
|
||||
logger: coreServices.logger,
|
||||
@@ -116,6 +141,7 @@ export const catalogPlugin = createBackendPlugin({
|
||||
Object.entries(processingExtensions.placeholderResolvers).forEach(
|
||||
([key, resolver]) => builder.setPlaceholderResolver(key, resolver),
|
||||
);
|
||||
builder.addLocationAnalyzers(...analysisExtensions.locationAnalyzers);
|
||||
|
||||
const { processingEngine, router } = await builder.build();
|
||||
|
||||
|
||||
@@ -8,8 +8,18 @@ import { CatalogProcessor } from '@backstage/plugin-catalog-node';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-node';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { PlaceholderResolver } from '@backstage/plugin-catalog-node';
|
||||
import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-node';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface CatalogAnalysisExtensionPoint {
|
||||
// (undocumented)
|
||||
addLocationAnalyzer(analyzer: ScmLocationAnalyzer): void;
|
||||
}
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const catalogAnalysisExtensionPoint: ExtensionPoint<CatalogAnalysisExtensionPoint>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface CatalogProcessingExtensionPoint {
|
||||
// (undocumented)
|
||||
|
||||
@@ -5,12 +5,19 @@
|
||||
```ts
|
||||
/// <reference types="node" />
|
||||
|
||||
import { AnalyzeLocationExistingEntity } from '@backstage/plugin-catalog-common';
|
||||
import { CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { LocationEntityV1alpha1 } from '@backstage/catalog-model';
|
||||
import { LocationSpec as LocationSpec_2 } from '@backstage/plugin-catalog-common';
|
||||
|
||||
// @public (undocumented)
|
||||
export type AnalyzeOptions = {
|
||||
url: string;
|
||||
catalogFilename?: string;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type CatalogProcessor = {
|
||||
getProcessorName(): string;
|
||||
@@ -198,4 +205,12 @@ export const processingResult: Readonly<{
|
||||
readonly relation: (spec: EntityRelationSpec) => CatalogProcessorResult;
|
||||
readonly refresh: (key: string) => CatalogProcessorResult;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ScmLocationAnalyzer = {
|
||||
supports(url: string): boolean;
|
||||
analyze(options: AnalyzeOptions): Promise<{
|
||||
existing: AnalyzeLocationExistingEntity[];
|
||||
}>;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -17,3 +17,5 @@
|
||||
export { catalogServiceRef } from './catalogService';
|
||||
export type { CatalogProcessingExtensionPoint } from './extensions';
|
||||
export { catalogProcessingExtensionPoint } from './extensions';
|
||||
export type { CatalogAnalysisExtensionPoint } from './extensions';
|
||||
export { catalogAnalysisExtensionPoint } from './extensions';
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
EntityProvider,
|
||||
CatalogProcessor,
|
||||
PlaceholderResolver,
|
||||
ScmLocationAnalyzer,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
|
||||
/**
|
||||
@@ -40,3 +41,18 @@ export const catalogProcessingExtensionPoint =
|
||||
createExtensionPoint<CatalogProcessingExtensionPoint>({
|
||||
id: 'catalog.processing',
|
||||
});
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export interface CatalogAnalysisExtensionPoint {
|
||||
addLocationAnalyzer(analyzer: ScmLocationAnalyzer): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export const catalogAnalysisExtensionPoint =
|
||||
createExtensionPoint<CatalogAnalysisExtensionPoint>({
|
||||
id: 'catalog.analysis',
|
||||
});
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
*/
|
||||
|
||||
export type {
|
||||
AnalyzeOptions,
|
||||
DeferredEntity,
|
||||
PlaceholderResolver,
|
||||
PlaceholderResolverParams,
|
||||
PlaceholderResolverRead,
|
||||
PlaceholderResolverResolveUrl,
|
||||
ScmLocationAnalyzer,
|
||||
} from './types';
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { AnalyzeLocationExistingEntity } from '@backstage/plugin-catalog-common';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { CatalogProcessorEmit } from '../api';
|
||||
|
||||
@@ -50,3 +51,20 @@ export type PlaceholderResolverParams = {
|
||||
export type PlaceholderResolver = (
|
||||
params: PlaceholderResolverParams,
|
||||
) => Promise<JsonValue>;
|
||||
|
||||
/** @public */
|
||||
export type AnalyzeOptions = {
|
||||
url: string;
|
||||
catalogFilename?: string;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type ScmLocationAnalyzer = {
|
||||
/** The method that decides if this analyzer can work with the provided url */
|
||||
supports(url: string): boolean;
|
||||
/** This function can return an array of already existing entities */
|
||||
analyze(options: AnalyzeOptions): Promise<{
|
||||
/** Existing entities in the analyzed location */
|
||||
existing: AnalyzeLocationExistingEntity[];
|
||||
}>;
|
||||
};
|
||||
|
||||
@@ -117,23 +117,39 @@ export class VisitsStorageApi implements VisitsApi {
|
||||
}
|
||||
|
||||
private async persistAll(visits: Array<Visit>) {
|
||||
const { userEntityRef } = await this.identityApi.getBackstageIdentity();
|
||||
const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`;
|
||||
|
||||
const storageKey = await this.getStorageKey();
|
||||
return this.storageApi.set<Array<Visit>>(storageKey, visits);
|
||||
}
|
||||
|
||||
private async retrieveAll(): Promise<Array<Visit>> {
|
||||
const storageKey = await this.getStorageKey();
|
||||
// Handles for case when snapshot is and is not referenced per storaged type used
|
||||
const snapshot = this.storageApi.snapshot<Array<Visit>>(storageKey);
|
||||
if (snapshot?.presence !== 'unknown') {
|
||||
return snapshot?.value ?? [];
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const subsription = this.storageApi
|
||||
.observe$<Visit[]>(storageKey)
|
||||
.subscribe({
|
||||
next: next => {
|
||||
const visits = next.value ?? [];
|
||||
subsription.unsubscribe();
|
||||
resolve(visits);
|
||||
},
|
||||
error: err => {
|
||||
subsription.unsubscribe();
|
||||
reject(err);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async getStorageKey(): Promise<string> {
|
||||
const { userEntityRef } = await this.identityApi.getBackstageIdentity();
|
||||
const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`;
|
||||
let visits: Array<Visit>;
|
||||
|
||||
try {
|
||||
visits = this.storageApi.snapshot<Array<Visit>>(storageKey).value ?? [];
|
||||
} catch {
|
||||
visits = [];
|
||||
}
|
||||
return visits;
|
||||
return storageKey;
|
||||
}
|
||||
|
||||
// This assumes Visit fields are either numbers or strings
|
||||
|
||||
@@ -19,23 +19,22 @@ import { useAnalytics } from '@backstage/core-plugin-api';
|
||||
import { useSearch } from '../../context';
|
||||
import usePrevious from 'react-use/lib/usePrevious';
|
||||
|
||||
function useFallingEdge(next: boolean) {
|
||||
const prev = usePrevious(next);
|
||||
return prev && !next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture search event on term change.
|
||||
*/
|
||||
export const TrackSearch = ({ children }: { children: React.ReactChild }) => {
|
||||
const useHasChanged = (value: any) => {
|
||||
const previousVal = usePrevious(value);
|
||||
return previousVal !== value;
|
||||
};
|
||||
|
||||
const analytics = useAnalytics();
|
||||
const { term, result } = useSearch();
|
||||
|
||||
const numberOfResults = result.value?.numberOfResults ?? undefined;
|
||||
|
||||
// Stops the analtyics event from firing before the new search engine response is returned
|
||||
const hasStartedLoading = useHasChanged(result.loading);
|
||||
const hasFinishedLoading = hasStartedLoading && !result.loading;
|
||||
const hasFinishedLoading = useFallingEdge(result.loading);
|
||||
|
||||
useEffect(() => {
|
||||
if (term && hasFinishedLoading) {
|
||||
|
||||
Reference in New Issue
Block a user