diff --git a/azure-pipelines.yaml b/azure-pipelines.yaml index d462bfd..3e62604 100644 --- a/azure-pipelines.yaml +++ b/azure-pipelines.yaml @@ -8,7 +8,7 @@ pr: variables: MajorVersion : 0 MinorVersion : 0 - PatchVersion : $[counter(format('nexus-extension-{0}.{1}.{2}', variables['Build.SourceBranchName'],variables['MajorVersion'],variables['MinorVersion']), 67)] + PatchVersion : $[counter(format('nexus-extension-{0}.{1}.{2}', variables['Build.SourceBranchName'],variables['MajorVersion'],variables['MinorVersion']), 78)] isPR : $[startsWith(variables['Build.SourceBranch'],'refs/pull/')] isMain : $[startsWith(variables['Build.SourceBranch'],'refs/heads/main')] diff --git a/package.json b/package.json index 7e07e20..fea2b7f 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,10 @@ "version": "0.0.1", "description": "Block Duplicate Work Items", "scripts": { - "build": "webpack", + "build": "webpack --mode production", + "build:dev": "webpack --mode development", "package": "npm run build && tfx extension create --manifests vss-extension.json --output-path ./packages/", + "package:dev": "npm run build:dev && tfx extension create --manifests vss-extension.json --output-path ./packages/", "lint": "eslint src --cache", "lintfix": "eslint src --fix --cache" }, @@ -14,7 +16,7 @@ "dependencies": { "azure-devops-extension-api": "^1.158.0", "azure-devops-extension-sdk": "^2.0.11", - "azure-devops-ui": "^2.167.0", + "azure-devops-ui": "^2.164.0", "react": "^17.0.0", "react-dom": "^17.0.0", "fast-dice-coefficient": "^1.0.3", diff --git a/src/block-duplicate-observer.ts b/src/block-duplicate-observer.ts index c2d8b4c..29db220 100644 --- a/src/block-duplicate-observer.ts +++ b/src/block-duplicate-observer.ts @@ -6,6 +6,7 @@ import { ILocationService, IExtensionDataService, IExtensionDataManager, + IProjectInfo, } from 'azure-devops-extension-api'; import { IWorkItemFormService, @@ -29,7 +30,7 @@ class duplicateObserver implements IWorkItemNotificationListener { _locationService: ILocationService; _projectService: IProjectPageService; _dataService: IExtensionDataService; - _timeout: NodeJS.Timeout; + _timeout: NodeJS.Timeout | undefined; _logger: Logger = new Logger(LogLevel.Info); _statusCodes = [503, 504]; _options = { @@ -55,7 +56,10 @@ class duplicateObserver implements IWorkItemNotificationListener { _fetch: ( input: RequestInfo, init?: fetchBuilder.RequestInitWithRetry - ) => Promise = fetchBuilder(originalFetch, this._options); + ) => Promise = fetchBuilder.default( + originalFetch.default, + this._options + ); constructor( workItemFormService: IWorkItemFormService, @@ -67,6 +71,7 @@ class duplicateObserver implements IWorkItemNotificationListener { this._locationService = locationService; this._projectService = projectService; this._dataService = dataService; + this._timeout = undefined; } // main entrypoint for validation logic @@ -100,7 +105,8 @@ class duplicateObserver implements IWorkItemNotificationListener { ); // Get The current ADO Project we need the project name later - const project = await this._projectService.getProject(); + const project: IProjectInfo | undefined = + await this._projectService.getProject(); // Get The WIT rest client const client: WorkItemTrackingRestClient = getClient( @@ -134,40 +140,44 @@ class duplicateObserver implements IWorkItemNotificationListener { const wiqlQuery = `SELECT [System.Id] FROM WorkItems WHERE [System.WorkItemType] = '${type}' AND [State] <> 'Closed' ORDER BY [System.CreatedDate] DESC`; this._logger.debug(`WIQL Query is '${wiqlQuery}'.`); - // Search for existing WI's which are not closed and are of the same type of the current WI - const wiqlResult: WorkItemQueryResult = await client.queryByWiql( - { - query: wiqlQuery, - }, - project.name - ); + let duplicate = false; - this._logger.debug(`WorkItem Count is '${wiqlResult.workItems.length}'.`); - - // Process the returned WI's in batches of 200 - const promises: Array> = [], - chunk = 200; - let i: number, j: number, chunk_items: Array; - - for (i = 0, j = wiqlResult.workItems.length; i < j; i += chunk) { - // Get The current batch - chunk_items = wiqlResult.workItems.slice(i, i + chunk); - // Setup our batch request payload we dont want everything only certain fields - promises.push( - this.validateWorkItemChunk( - hostBaseUrl, - project.name, - id, - this.normalizeString(title), - this.normalizeString(description), - similarityIndex, - chunk_items - ) + if (project) { + // Search for existing WI's which are not closed and are of the same type of the current WI + const wiqlResult: WorkItemQueryResult = await client.queryByWiql( + { + query: wiqlQuery, + }, + project.name ); - } - // Wait for any one of our promises to return bool(true) result then continue - const duplicate: boolean = await this.getfirstResolvedPromise(promises); + this._logger.debug(`WorkItem Count is '${wiqlResult.workItems.length}'.`); + + // Process the returned WI's in batches of 200 + const promises: Array> = [], + chunk = 200; + let i: number, j: number, chunk_items: Array; + + for (i = 0, j = wiqlResult.workItems.length; i < j; i += chunk) { + // Get The current batch + chunk_items = wiqlResult.workItems.slice(i, i + chunk); + // Setup our batch request payload we dont want everything only certain fields + promises.push( + this.validateWorkItemChunk( + hostBaseUrl, + project.name, + id, + this.normalizeString(title), + this.normalizeString(description), + similarityIndex, + chunk_items + ) + ); + } + + // Wait for any one of our promises to return bool(true) result then continue + duplicate = await this.getfirstResolvedPromise(promises); + } // Check if we have any other invalid fields const invalidFields = await this._workItemFormService.getInvalidFields(); @@ -201,7 +211,8 @@ class duplicateObserver implements IWorkItemNotificationListener { // Remove things we dont want to compare on and ensure comparison based on lower case strings private normalizeString(orignial_text: string): string { if (orignial_text && orignial_text !== '') - return striptags(orignial_text) + return striptags + .default(orignial_text) .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, '') // !"#$%&'()*+,-./:;?@[\]^_`{|}~ .replace(/\s{2,}/g, ' ') .trim() diff --git a/src/block-duplicate-project-admin.tsx b/src/block-duplicate-project-admin.tsx index 98bbcac..591ff9b 100644 --- a/src/block-duplicate-project-admin.tsx +++ b/src/block-duplicate-project-admin.tsx @@ -1,10 +1,10 @@ import './block-duplicate-project-admin.scss'; +import { Header, TitleSize } from 'azure-devops-ui/Header'; +import { Page } from 'azure-devops-ui/Page'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import * as SDK from 'azure-devops-extension-sdk'; import Logger, { LogLevel } from './logger'; -import { Header } from 'azure-devops-ui/Header'; -import { Page } from 'azure-devops-ui/Page'; import { CommonServiceIds, IExtensionDataService, @@ -60,15 +60,12 @@ class BlockDuplicatesAdmin extends React.Component< public render(): JSX.Element { return ( - -
-
-

Similarity Index

-

- {this.state.SimilarityIndex} -

-
- +
+

Similarity Index

+

+ {this.state.SimilarityIndex} +

+
); } } diff --git a/tsconfig.json b/tsconfig.json index 8f5afe2..da5c9a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,26 @@ { "compilerOptions": { - "rootDir": "./src/", - "outDir": "./dist/", - "moduleResolution": "node", - "noImplicitAny": true, - "module": "es6", - "target": "es5", - "allowJs": true, - "jsx": "react", - }, - "exclude": [ - "packages", - "dist", - "node_modules", - "webpack.config.js" - ], - "types": [ - "react", - "react-dom" - ] - } \ No newline at end of file + "charset": "utf8", + "experimentalDecorators": true, + "module": "amd", + "moduleResolution": "node", + "noImplicitAny": true, + "noImplicitThis": true, + "strict": true, + "target": "es5", + "rootDir": "src/", + "outDir": "dist/", + "jsx": "react", + "lib": [ + "es5", + "es6", + "dom", + "es2015.promise" + ], + "types": [ + "react", + "node" + ], + "esModuleInterop": true + } +} \ No newline at end of file diff --git a/vss-extension.json b/vss-extension.json index a8b1563..f3e6340 100644 --- a/vss-extension.json +++ b/vss-extension.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "id": "block-duplicate-work-items", "publisher": "soft-cor", - "version": "0.0.24", + "version": "0.0.77", "name": "Block Duplicate Work Items", "description": "Block Duplicate Work Items", "public": true, @@ -76,7 +76,7 @@ "ms.vss-web.project-admin-hub-group" ], "properties": { - "name": "Block Duplicate Work Items Admin Hub", + "name": "Block Duplicate Work Items", "order": 100, "uri": "block-duplicate-project-admin.html" } diff --git a/webpack.config.js b/webpack.config.js index b73d85b..6ca04c8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,7 +3,6 @@ const TerserPlugin = require('terser-webpack-plugin'); const webpack = require('webpack'); module.exports = { - mode: "production", target: "web", entry: { 'block-duplicate-observer': './src/block-duplicate-observer.ts', @@ -52,6 +51,9 @@ module.exports = { }, resolve: { extensions: [ '.html','.css','.scss', '.ts', '.tsx', '.js' ], + alias: { + "azure-devops-extension-sdk": path.resolve("node_modules/azure-devops-extension-sdk") + }, }, output: { path: path.resolve(__dirname, 'dist'),