Skip to content

Commit

Permalink
Merge pull request #52 from keyoke/keyoke/features
Browse files Browse the repository at this point in the history
updates
  • Loading branch information
keyoke authored Oct 5, 2021
2 parents d548aee + 7896ec8 commit 080deb5
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 72 deletions.
2 changes: 1 addition & 1 deletion azure-pipelines.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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')]

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand All @@ -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",
Expand Down
81 changes: 46 additions & 35 deletions src/block-duplicate-observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ILocationService,
IExtensionDataService,
IExtensionDataManager,
IProjectInfo,
} from 'azure-devops-extension-api';
import {
IWorkItemFormService,
Expand All @@ -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 = {
Expand All @@ -55,7 +56,10 @@ class duplicateObserver implements IWorkItemNotificationListener {
_fetch: (
input: RequestInfo,
init?: fetchBuilder.RequestInitWithRetry
) => Promise<Response> = fetchBuilder(originalFetch, this._options);
) => Promise<Response> = fetchBuilder.default(
originalFetch.default,
this._options
);

constructor(
workItemFormService: IWorkItemFormService,
Expand All @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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<Promise<boolean>> = [],
chunk = 200;
let i: number, j: number, chunk_items: Array<WorkItemReference>;

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<Promise<boolean>> = [],
chunk = 200;
let i: number, j: number, chunk_items: Array<WorkItemReference>;

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();
Expand Down Expand Up @@ -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()
Expand Down
19 changes: 8 additions & 11 deletions src/block-duplicate-project-admin.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -60,15 +60,12 @@ class BlockDuplicatesAdmin extends React.Component<

public render(): JSX.Element {
return (
<Page className="block-duplicate-observer-project-admin-hub flex-grow">
<Header title="Block Duplicate Work Items Admin Hub" />
<div>
<h3>Similarity Index</h3>
<p>
<strong>{this.state.SimilarityIndex}</strong>
</p>
</div>
</Page>
<div>
<h3>Similarity Index</h3>
<p>
<strong>{this.state.SimilarityIndex}</strong>
</p>
</div>
);
}
}
Expand Down
44 changes: 24 additions & 20 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
"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
}
}
4 changes: 2 additions & 2 deletions vss-extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"
}
Expand Down
4 changes: 3 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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'),
Expand Down

0 comments on commit 080deb5

Please sign in to comment.