Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump azure-devops-extension-sdk from 2.0.11 to 4.0.2 in /src/frontend #1103

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/frontend/components/actionItem.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import { DefaultButton, IButtonProps, PrimaryButton, BaseButton, Button } from 'office-ui-fabric-react/lib/Button';
import { DocumentCard, DocumentCardActions, DocumentCardTitle, DocumentCardType, DocumentCardPreview, IDocumentCardPreviewProps } from 'office-ui-fabric-react/lib/DocumentCard';
import { getService } from 'azure-devops-extension-sdk';
import { WorkItem, WorkItemType, WorkItemStateColor } from 'azure-devops-extension-api/WorkItemTracking/WorkItemTracking';
import { WorkItemTrackingServiceIds, IWorkItemFormNavigationService } from 'azure-devops-extension-api/WorkItemTracking';

Expand All @@ -11,6 +10,7 @@ import { IFeedbackItemDocument } from '../interfaces/feedback';
import Dialog, { DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { withAITracking } from '@microsoft/applicationinsights-react-js';
import { reactPlugin } from '../utilities/telemetryClient';
import { SDKContext } from '../dal/azureDevOpsContextProvider';

export interface ActionItemProps extends IButtonProps {
feedbackItemId: string;
Expand Down Expand Up @@ -70,7 +70,8 @@ class ActionItem extends React.Component<ActionItemProps, ActionItemState> {
}

private readonly onActionItemClick = async (workItemId: number) => {
const workItemNavSvc = await getService<IWorkItemFormNavigationService>(WorkItemTrackingServiceIds.WorkItemFormNavigationService);
const { SDK } = React.useContext(SDKContext);
const workItemNavSvc = await SDK.getService<IWorkItemFormNavigationService>(WorkItemTrackingServiceIds.WorkItemFormNavigationService);

await workItemNavSvc.openWorkItem(workItemId);

Expand Down
7 changes: 4 additions & 3 deletions src/frontend/components/actionItemDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { WebApiTeam } from 'azure-devops-extension-api/Core';
import { IWorkItemFormNavigationService, WorkItemTrackingServiceIds } from 'azure-devops-extension-api/WorkItemTracking';
import { WorkItem, WorkItemType } from 'azure-devops-extension-api/WorkItemTracking/WorkItemTracking';
import { getService, getUser } from 'azure-devops-extension-sdk';
import { ActionButton, BaseButton, Button, DefaultButton, IButtonProps, PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import { Image } from 'office-ui-fabric-react/lib/Image';
import React from 'react';
Expand All @@ -17,6 +16,7 @@ import { IFeedbackItemDocument } from '../interfaces/feedback';
import { getBoardUrl } from '../utilities/boardUrlHelper';
import { appInsights, reactPlugin, TelemetryEvents } from '../utilities/telemetryClient';
import ActionItem from './actionItem';
import { SDKContext } from '../dal/azureDevOpsContextProvider';

export interface ActionItemDisplayProps extends IButtonProps {
feedbackItemId: string;
Expand Down Expand Up @@ -66,11 +66,12 @@ class ActionItemDisplay extends React.Component<ActionItemDisplayProps, ActionIt
private addActionItemButtonWrapper: HTMLElement | null;

private readonly createAndLinkActionItem = async (workItemTypeName: string) => {
const { SDK } = React.useContext(SDKContext);
const boardUrl = await getBoardUrl(this.props.team.id, this.props.boardId);
const workItemNavSvc = await getService<IWorkItemFormNavigationService>(WorkItemTrackingServiceIds.WorkItemFormNavigationService);
const workItemNavSvc = await SDK.getService<IWorkItemFormNavigationService>(WorkItemTrackingServiceIds.WorkItemFormNavigationService);

// Account for any users who are no longer a part of the org
const assignedUser: string | undefined = getUser().name === undefined ? "Former User" : getUser().name;
const assignedUser: string | undefined = SDK.getUser().name === undefined ? "Former User" : SDK.getUser().name;

const workItem = await workItemNavSvc.openNewWorkItem(workItemTypeName, {
'System.AssignedTo': assignedUser,
Expand Down
8 changes: 5 additions & 3 deletions src/frontend/components/boardSummary.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import { getService } from 'azure-devops-extension-sdk';
import { WorkItem, WorkItemType } from 'azure-devops-extension-api/WorkItemTracking/WorkItemTracking';
import { DocumentCard, DocumentCardTitle, DocumentCardType } from 'office-ui-fabric-react/lib/DocumentCard';
import { Image } from 'office-ui-fabric-react/lib/Image';
import { WorkItemTrackingServiceIds, IWorkItemFormNavigationService } from 'azure-devops-extension-api/WorkItemTracking';
import { DetailsList, DetailsListLayoutMode, SelectionMode, IColumn } from 'office-ui-fabric-react/lib/DetailsList';
import { withAITracking } from '@microsoft/applicationinsights-react-js';
import { reactPlugin } from '../utilities/telemetryClient';
import { SDKContext } from '../dal/azureDevOpsContextProvider';

export interface IBoardSummaryProps {
actionItems: WorkItem[];
Expand Down Expand Up @@ -180,7 +180,8 @@ class BoardSummary extends React.Component<IBoardSummaryProps, IBoardSummaryStat
priority: workItem.fields['Microsoft.VSTS.Common.Priority'],
id: workItem.id,
onActionItemClick: async (id: number) => {
const workItemNavSvc = await getService<IWorkItemFormNavigationService>(WorkItemTrackingServiceIds.WorkItemFormNavigationService);
const { SDK } = React.useContext(SDKContext);
const workItemNavSvc = await SDK.getService<IWorkItemFormNavigationService>(WorkItemTrackingServiceIds.WorkItemFormNavigationService);
await workItemNavSvc.openWorkItem(id);
}
Comment on lines 182 to 186
Copy link
Preview

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that using React.useContext within this callback complies with the Rules of Hooks. If this function is not a React component or custom hook, refactor accordingly.

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

};
Expand Down Expand Up @@ -217,7 +218,8 @@ class BoardSummary extends React.Component<IBoardSummaryProps, IBoardSummaryStat
}

private readonly onItemInvoked = async (item: { id: number }) => {
const workItemNavSvc = await getService<IWorkItemFormNavigationService>(WorkItemTrackingServiceIds.WorkItemFormNavigationService);
const { SDK } = React.useContext(SDKContext);
const workItemNavSvc = await SDK.getService<IWorkItemFormNavigationService>(WorkItemTrackingServiceIds.WorkItemFormNavigationService);
Comment on lines +221 to +222
Copy link
Preview

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using React.useContext inside this event handler might violate hook rules if it isn’t within a proper React component context. Consider refactoring to retrieve context once within the component.

Suggested change
const { SDK } = React.useContext(SDKContext);
const workItemNavSvc = await SDK.getService<IWorkItemFormNavigationService>(WorkItemTrackingServiceIds.WorkItemFormNavigationService);
const workItemNavSvc = await this.sdk.getService<IWorkItemFormNavigationService>(WorkItemTrackingServiceIds.WorkItemFormNavigationService);

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

await workItemNavSvc.openWorkItem(item.id);
}

Expand Down
8 changes: 5 additions & 3 deletions src/frontend/components/feedbackBoardContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ import copyToClipboard from 'copy-to-clipboard';
import { getColumnsByTemplateId } from '../utilities/boardColumnsHelper';
import { FeedbackBoardPermissionOption } from './feedbackBoardMetadataFormPermissions';
import { CommonServiceIds, IHostNavigationService } from 'azure-devops-extension-api/Common/CommonServices';
import { getService } from 'azure-devops-extension-sdk';
import { FontIcon } from 'office-ui-fabric-react';
import { SDKContext } from '../dal/azureDevOpsContextProvider';

export interface FeedbackBoardContainerProps {
isHostedAzureDevOps: boolean;
Expand Down Expand Up @@ -252,13 +252,15 @@ class FeedbackBoardContainer extends React.Component<FeedbackBoardContainerProps
}

private async updateUrlWithBoardAndTeamInformation(teamId: string, boardId: string) {
getService<IHostNavigationService>(CommonServiceIds.HostNavigationService).then(service => {
const { SDK } = React.useContext(SDKContext);
Copy link
Preview

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While integrating SDKContext in a component, ensure that React.useContext is used within the render flow. Verify that this usage adheres to hook rules given the function’s context.

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

SDK.getService<IHostNavigationService>(CommonServiceIds.HostNavigationService).then(service => {
service.setHash(`teamId=${teamId}&boardId=${boardId}`);
});
}

private async parseUrlForBoardAndTeamInformation(): Promise<{ teamId: string, boardId: string }> {
const service = await getService<IHostNavigationService>(CommonServiceIds.HostNavigationService);
const { SDK } = React.useContext(SDKContext);
Copy link
Preview

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeated use of React.useContext in the same component should be consolidated if possible. Consider retrieving the SDK context once and reusing it to avoid potential misuse of hooks.

Suggested change
const { SDK } = React.useContext(SDKContext);
const { SDK } = this.sdkContext;

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

const service = await SDK.getService<IHostNavigationService>(CommonServiceIds.HostNavigationService);
let hash = await service.getHash();
if (hash.startsWith('#')) {
hash = hash.substring(1);
Expand Down
26 changes: 26 additions & 0 deletions src/frontend/dal/azureDevOpsContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as SDK from 'azure-devops-extension-sdk';
import React from 'react';

type SDKContextProps = {
SDK: typeof SDK;
};

export const SDKContext = React.createContext<SDKContextProps>({} as SDKContextProps);

export const SDKProvider: React.FC = (props) => {
const context: SDKContextProps = {
SDK
};

const initSDK = async () => {
await SDK.init();
};

React.useEffect(() => {
initSDK();
}, []);

return (
<SDKContext.Provider value={context}>{props.children}</SDKContext.Provider>
);
};
10 changes: 6 additions & 4 deletions src/frontend/dal/dataService.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { getAccessToken, getExtensionContext, getService } from 'azure-devops-extension-sdk';
import { CommonServiceIds, IExtensionDataManager, IExtensionDataService } from 'azure-devops-extension-api';
import { appInsights } from '../utilities/telemetryClient';
import { SDKContext } from './azureDevOpsContextProvider';
import React from 'react';

let extensionDataManager: IExtensionDataManager;

async function getDataService(): Promise<IExtensionDataManager> {
if (!extensionDataManager) {
const accessToken = await getAccessToken();
const extensionDataService = await getService<IExtensionDataService>(CommonServiceIds.ExtensionDataService);
extensionDataManager = await extensionDataService.getExtensionDataManager(getExtensionContext().id, accessToken);
const { SDK } = React.useContext(SDKContext);
const accessToken = await SDK.getAccessToken();
Comment on lines 8 to +10
Copy link
Preview

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly using React.useContext in this utility function may breach React Hook rules. Refactor to use a custom hook or pass the SDK context from a parent component.

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

const extensionDataService = await SDK.getService<IExtensionDataService>(CommonServiceIds.ExtensionDataService);
extensionDataManager = await extensionDataService.getExtensionDataManager(SDK.getExtensionContext().id, accessToken);
}

return extensionDataManager;
Expand Down
7 changes: 5 additions & 2 deletions src/frontend/dal/reflectBackendService.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React from 'react';
import { HubConnection, HubConnectionBuilder, LogLevel } from '@microsoft/signalr';
import moment from 'moment';
import { getAppToken } from 'azure-devops-extension-sdk';

import { config } from '../config/config';
import { decodeJwt } from '../utilities/tokenHelper';
import { isHostedAzureDevOps } from '../utilities/azureDevOpsContextHelper';
import { appInsights } from '../utilities/telemetryClient';
import { IExceptionTelemetry } from '@microsoft/applicationinsights-web';
import { SDKContext } from './azureDevOpsContextProvider';

const enum ReflectBackendSignals {
JoinReflectBoardGroup = 'joinReflectBoardGroup',
Expand Down Expand Up @@ -81,7 +82,9 @@ class ReflectBackendService {
return that._appToken;
}

return Promise.resolve(getAppToken().then((appToken) => {
const { SDK } = React.useContext(SDKContext);
Copy link
Preview

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invoking React.useContext in a non-component function could violate hook rules. Consider refactoring this logic into a custom hook or ensuring it’s used within a valid React component.

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.


return Promise.resolve(SDK.getAppToken().then((appToken) => {
that._appToken = appToken;

const tokenData = decodeJwt(that._appToken);
Expand Down
7 changes: 5 additions & 2 deletions src/frontend/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import './css/main.scss';
import { reactPlugin } from './utilities/telemetryClient';
import { AppInsightsErrorBoundary } from '@microsoft/applicationinsights-react-js';
import FeedbackBoardContainer, { FeedbackBoardContainerProps } from './components/feedbackBoardContainer';
import { SDKProvider } from './dal/azureDevOpsContextProvider';

initializeIcons('https://res.cdn.office.net/files/fabric-cdn-prod_20240129.001/assets/icons/');

Expand All @@ -20,9 +21,11 @@ sdkInit({ applyTheme: true }).then(() => {
};

ReactDOM.render(
<AppInsightsErrorBoundary onError={() => <h1>We detected an error in the application</h1>} appInsights={reactPlugin}>
<SDKProvider>
<AppInsightsErrorBoundary onError={() => <h1>We detected an error in the application</h1>} appInsights={reactPlugin}>
<FeedbackBoardContainer {...feedbackBoardContainerProps} />
</AppInsightsErrorBoundary>,
</AppInsightsErrorBoundary>
</SDKProvider>,
document.getElementById('root') as HTMLElement,
);
});
Expand Down
20 changes: 16 additions & 4 deletions src/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"@microsoft/signalr": "8.0.7",
"@tanstack/react-table": "8.20.5",
"azure-devops-extension-api": "1.152.0",
"azure-devops-extension-sdk": "2.0.11",
"azure-devops-extension-sdk": "4.0.2",
"classnames": "2.5.1",
"copy-to-clipboard": "3.3.3",
"dotenv-webpack": "8.1.0",
Expand Down
6 changes: 4 additions & 2 deletions src/frontend/utilities/azureDevOpsContextHelper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getHost } from 'azure-devops-extension-sdk';
import React from 'react';
import { SDKContext } from '../dal/azureDevOpsContextProvider';
import { getHostAuthority } from '../utilities/servicesHelper';

const internalOrgNames = [
Expand All @@ -15,7 +16,8 @@ const internalOrgNames = [
* Returns whether the current org in VSTS context is a recognized internal org.
*/
export const isInternalOrg = () => {
const host = getHost();
Copy link
Preview

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using React.useContext in a non-component function may result in unexpected behavior due to hook rules. Refactor this access to occur within a functional component or custom hook.

Suggested change
export const isInternalOrg = () => {
export const useIsInternalOrg = () => {

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

const { SDK } = React.useContext(SDKContext);
const host = SDK.getHost();
return internalOrgNames.indexOf(host.name.toLowerCase().trim()) !== -1;
};

Expand Down
9 changes: 6 additions & 3 deletions src/frontend/utilities/servicesHelper.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { getService } from 'azure-devops-extension-sdk';
import { CommonServiceIds, IProjectPageService, IProjectInfo, ILocationService } from 'azure-devops-extension-api';
import { CoreRestClient } from 'azure-devops-extension-api/Core';
import React from 'react';
import { SDKContext } from '../dal/azureDevOpsContextProvider';

/**
* Get the project info
*/
const getProjectInfo = async (): Promise<IProjectInfo> => {
const projectPageService = await getService<IProjectPageService>(CommonServiceIds.ProjectPageService);
const { SDK } = React.useContext(SDKContext);
const projectPageService = await SDK.getService<IProjectPageService>(CommonServiceIds.ProjectPageService);
Copy link
Preview

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using React.useContext in a utility function (outside of a React component) can lead to hook rule violations. Consider moving this logic into a custom hook or passing the context as an argument.

Suggested change
const { SDK } = React.useContext(SDKContext);
const SDK = useSDK();

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.


return projectPageService.getProject();
}
Expand All @@ -33,7 +35,8 @@ export const getProjectId = async (): Promise<string> => {
* Get the host base URL
*/
export const getHostBaseUrl = async (): Promise<string> => {
const locationService = await getService<ILocationService>(CommonServiceIds.LocationService);
const { SDK } = React.useContext(SDKContext);
const locationService = await SDK.getService<ILocationService>(CommonServiceIds.LocationService);
const hostBaseUrl = await locationService.getResourceAreaLocation(
CoreRestClient.RESOURCE_AREA_ID
);
Expand Down
6 changes: 4 additions & 2 deletions src/frontend/utilities/userIdentityHelper.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { IdentityRef } from 'azure-devops-extension-api/WebApi';
import { IUserContext, getUser } from 'azure-devops-extension-sdk';
import { SDKContext } from '../dal/azureDevOpsContextProvider';

let userIdentity: IdentityRef;

Expand All @@ -8,7 +9,8 @@ let userIdentity: IdentityRef;
*/
export const getUserIdentity = (): IdentityRef => {
if (!userIdentity){
const currentUser: IUserContext = getUser();
const { SDK } = React.useContext(SDKContext);
Copy link
Preview

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using React.useContext inside a non-component function may violate React Hooks rules. Consider refactoring this utility to a custom hook or ensuring it’s only called within a functional component.

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

const currentUser = SDK.getUser();

userIdentity = {
id: currentUser.id,
Expand Down
Loading