devops-plugin-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@danieli-automation/create-devops-plugin/package.json +30 -0
- package/@danieli-automation/create-devops-plugin/src/cli.ts +136 -0
- package/@danieli-automation/create-devops-plugin/src/index.ts +37 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/apiIndexMock.ts +10 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/appHtml.ts +25 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/appStyles.ts +22 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/appTsx.ts +41 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/envExample.ts +11 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/gitignore.ts +13 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/index.ts +51 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/packageJson.ts +60 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/readme.ts +30 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/sdkMock.ts +11 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/testSetup.ts +16 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/tsconfigJson.ts +30 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/vitestConfig.ts +57 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/webpackAppConfig.ts +31 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/webpackCommonConfig.ts +116 -0
- package/@danieli-automation/create-devops-plugin/src/template/files/webpackConfig.ts +15 -0
- package/@danieli-automation/create-devops-plugin/src/template/files.ts +1 -0
- package/@danieli-automation/create-devops-plugin/src/template/fs.ts +85 -0
- package/@danieli-automation/create-devops-plugin/src/template/manifest.ts +40 -0
- package/@danieli-automation/create-devops-plugin/src/template/npm.ts +22 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/fonts/AzDevMDL2.woff +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/fonts/bowtie.woff2 +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/fonts/fabric-icons.woff +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/fonts/fluent-filled-v1.1.293.woff2 +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/fonts/fluent-regular-v1.1.293.woff2 +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/images/DigiMetLogo.jpeg +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/images/danieliAutomation.png +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/images/danieliAutomationBlack.jpg +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/images/danieli_digi_met_logo.jpeg +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/static/images/logoSmallpng.png +0 -0
- package/@danieli-automation/create-devops-plugin/src/template/types.ts +14 -0
- package/@danieli-automation/create-devops-plugin/src/template/utils.ts +22 -0
- package/@danieli-automation/create-devops-plugin/tsconfig.json +8 -0
- package/@danieli-automation/devops-plugin-core/package.json +27 -0
- package/@danieli-automation/devops-plugin-core/src/core/azureClients.ts +18 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/createStore.ts +65 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/hooks/useCrossTeamSprintInstance.ts +145 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/hooks/useTaskOrder.ts +125 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/hooks/useWorkItemOrder.ts +86 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/index.ts +13 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/keys.ts +31 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/repositories/instance.ts +184 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/repositories/taskOrder.ts +59 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/repositories/workItemOrder.ts +60 -0
- package/@danieli-automation/devops-plugin-core/src/core/storage/stores.ts +18 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/AdoWorkItemType.ts +18 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/KVStoreType.ts +1 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/ScopeType.ts +1 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/SelectedProjectType.ts +8 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/SortConfigType.ts +2 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/instance/CreateInstanceInputType.ts +10 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/instance/CrossSprintInstanceType.ts +20 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/instance/DefaultInstanceType.ts +12 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/instance/InstanceRowType.ts +18 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/instance/UpdateInstanceInputType.ts +10 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/taskOrder/TaskOrderMapType.ts +3 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/taskOrder/TaskOrderType.ts +1 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/workItemOrder/WorkItemOrderMapType.ts +3 -0
- package/@danieli-automation/devops-plugin-core/src/core/types/workItemOrder/WorkItemOrderType.ts +1 -0
- package/@danieli-automation/devops-plugin-core/src/index.ts +1 -0
- package/@danieli-automation/devops-plugin-core/src/pluginCore.ts +12 -0
- package/@danieli-automation/devops-plugin-core/tsconfig.json +16 -0
- package/@danieli-automation/devops-plugin-features/package.json +31 -0
- package/@danieli-automation/devops-plugin-features/src/app/stores/useUIStore.ts +12 -0
- package/@danieli-automation/devops-plugin-features/src/app/utils/date.ts +9 -0
- package/@danieli-automation/devops-plugin-features/src/app/utils/global.ts +9 -0
- package/@danieli-automation/devops-plugin-features/src/core/azureClients.ts +12 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/constants/InstanceConstant.ts +5 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/hooks/useInstancePermission.ts +127 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/stores/__tests__/useInstanceStore.test.ts +25 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/stores/types/InstanceStoreType.ts +7 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/stores/useInstanceStore.ts +9 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/types/CrossSprintInstanceType.ts +20 -0
- package/@danieli-automation/devops-plugin-features/src/features/instances/utils/instance.ts +55 -0
- package/@danieli-automation/devops-plugin-features/src/features/iterations/api/iterations.ts +298 -0
- package/@danieli-automation/devops-plugin-features/src/features/iterations/services/IterationService.ts +215 -0
- package/@danieli-automation/devops-plugin-features/src/features/iterations/types/IterationInfoType.ts +15 -0
- package/@danieli-automation/devops-plugin-features/src/features/iterations/utils/__tests__/iteration.test.ts +39 -0
- package/@danieli-automation/devops-plugin-features/src/features/iterations/utils/iteration.ts +132 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/api/projects.ts +79 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/api/teams.ts +29 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/api/users.ts +124 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/services/ProjectService.ts +80 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/types/SelectedProjectType.ts +8 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/types/TeamMemberType.ts +7 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/types/TeamRowSeedType.ts +6 -0
- package/@danieli-automation/devops-plugin-features/src/features/teams/types/UserSearchResultType.ts +8 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/api/states.ts +24 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/api/wiql.ts +146 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/api/workItems.ts +193 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/DefaultConsant.ts +43 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/FieldConstant.ts +27 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/StateConstant.ts +16 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/constants/TypeConstant.ts +8 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/services/WIQLService.ts +101 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/services/WorkItemService.ts +54 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/types/AdoWorkItemType.ts +18 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/__tests__/filter.test.ts +87 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/__tests__/workItem.test.ts +116 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/filter.ts +189 -0
- package/@danieli-automation/devops-plugin-features/src/features/work-items/utils/workItem.ts +245 -0
- package/@danieli-automation/devops-plugin-features/src/index.ts +0 -0
- package/@danieli-automation/devops-plugin-features/src/test/mocks/azure-devops-extension-api/Work.ts +5 -0
- package/@danieli-automation/devops-plugin-features/tsconfig.json +18 -0
- package/@danieli-automation/devops-plugin-features/vitest.config.ts +21 -0
- package/README.md +55 -0
- package/devops-plugin-kit-0.1.0.tgz +0 -0
- package/package.json +29 -0
- package/tsconfig.base.json +15 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type ListBoxItem = {
|
|
2
|
+
id?: string;
|
|
3
|
+
text?: string;
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
type DropdownSelection = unknown;
|
|
8
|
+
|
|
9
|
+
export type InstanceRowType = {
|
|
10
|
+
id: string;
|
|
11
|
+
selectedProjectKey?: string;
|
|
12
|
+
projectName?: string;
|
|
13
|
+
selectedTeamKey?: string;
|
|
14
|
+
teamName?: string;
|
|
15
|
+
teams: ListBoxItem[];
|
|
16
|
+
projectSelection: DropdownSelection;
|
|
17
|
+
teamSelection: DropdownSelection;
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type TaskOrderType = { id: number; parentId: number; order: number };
|
package/@danieli-automation/devops-plugin-core/src/core/types/workItemOrder/WorkItemOrderType.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type WorkItemOrderType = { id: number; order: number };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./pluginCore.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "dist",
|
|
5
|
+
"rootDir": "src",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "Node",
|
|
8
|
+
"noImplicitAny": false,
|
|
9
|
+
"baseUrl": ".",
|
|
10
|
+
"paths": {
|
|
11
|
+
"core/*": ["src/core/*"]
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"],
|
|
15
|
+
"exclude": ["**/__tests__/**"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@danieli-automation/devops-plugin-features",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared feature-level state, hooks, and helpers for Azure DevOps plugins",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"clean": "Remove-Item -Recurse -Force dist -ErrorAction SilentlyContinue",
|
|
21
|
+
"lint": "echo lint not configured",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"vitest": "^3.2.4"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "restricted"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type UIUser = {
|
|
2
|
+
id?: string;
|
|
3
|
+
descriptor?: string;
|
|
4
|
+
} | null;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Temporary package-level UI store hook.
|
|
8
|
+
* Consumers can replace or wrap this with application-specific state.
|
|
9
|
+
*/
|
|
10
|
+
export function useUIStore(): { currentUser: UIUser } {
|
|
11
|
+
return { currentUser: null };
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a date-like input to YYYY-MM-DD string.
|
|
3
|
+
*/
|
|
4
|
+
export function toIsoDate(value: string | Date | null | undefined): string {
|
|
5
|
+
if (!value) return "";
|
|
6
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
7
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
8
|
+
return date.toISOString().slice(0, 10);
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts the first path segment as project name.
|
|
3
|
+
*/
|
|
4
|
+
export function extractProjectFromPath(pathValue: string | null | undefined): string {
|
|
5
|
+
if (!pathValue) return "";
|
|
6
|
+
const normalized = pathValue.replace(/\\/g, "/");
|
|
7
|
+
const [project] = normalized.split("/");
|
|
8
|
+
return project ?? "";
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getClient } from "azure-devops-extension-api";
|
|
2
|
+
import { CoreRestClient } from "azure-devops-extension-api/Core";
|
|
3
|
+
import { GitRestClient } from "azure-devops-extension-api/Git";
|
|
4
|
+
import { GraphRestClient } from "azure-devops-extension-api/Graph";
|
|
5
|
+
import { WorkRestClient } from "azure-devops-extension-api/Work";
|
|
6
|
+
import { WorkItemTrackingRestClient } from "azure-devops-extension-api/WorkItemTracking";
|
|
7
|
+
|
|
8
|
+
export const coreClient = () => getClient(CoreRestClient);
|
|
9
|
+
export const witClient = () => getClient(WorkItemTrackingRestClient);
|
|
10
|
+
export const workClient = () => getClient(WorkRestClient);
|
|
11
|
+
export const gitClient = () => getClient(GitRestClient);
|
|
12
|
+
export const graphClient = () => getClient(GraphRestClient);
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useUIStore } from "app/stores/useUIStore";
|
|
2
|
+
import { GraphGroup } from "azure-devops-extension-api/Graph";
|
|
3
|
+
import { graphClient } from "core/azureClients";
|
|
4
|
+
import { useInstanceStore } from "features/instances/stores/useInstanceStore";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
|
|
7
|
+
type RoleState = {
|
|
8
|
+
isAdmin: boolean;
|
|
9
|
+
isJM: boolean;
|
|
10
|
+
groups: string[];
|
|
11
|
+
loading: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Centralized permission logic for instances.
|
|
16
|
+
* - canReorder = true -> user can drag & drop backlog order
|
|
17
|
+
* - canReorder = false -> instance is active and user is NOT a JM
|
|
18
|
+
*/
|
|
19
|
+
export function useInstancePermission() {
|
|
20
|
+
const { currentInstance } = useInstanceStore();
|
|
21
|
+
const { currentUser } = useUIStore();
|
|
22
|
+
const isInstanceMode = !!currentInstance?.id;
|
|
23
|
+
|
|
24
|
+
const [roles, setRoles] = React.useState<RoleState>({
|
|
25
|
+
isAdmin: false,
|
|
26
|
+
isJM: false,
|
|
27
|
+
groups: [],
|
|
28
|
+
loading: false,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/*React.useEffect(() => {
|
|
32
|
+
if (!currentUser) return;
|
|
33
|
+
|
|
34
|
+
let mounted = true;
|
|
35
|
+
|
|
36
|
+
(async () => {
|
|
37
|
+
const result = await resolveUserRoles(currentUser);
|
|
38
|
+
if (mounted) setRoles({ ...result, loading: false });
|
|
39
|
+
})();
|
|
40
|
+
|
|
41
|
+
return () => {
|
|
42
|
+
mounted = false;
|
|
43
|
+
};
|
|
44
|
+
}, [currentUser]);*/
|
|
45
|
+
|
|
46
|
+
// Owner logic is **instance-specific**
|
|
47
|
+
|
|
48
|
+
const canEditInstance = React.useMemo(() => {
|
|
49
|
+
if (!isInstanceMode || !currentUser) return false;
|
|
50
|
+
const currentDescriptor = currentUser?.descriptor;
|
|
51
|
+
const createdByDescriptor = currentInstance.createdBy?.descriptor;
|
|
52
|
+
return currentInstance.createdBy.id === currentUser.id ||
|
|
53
|
+
(!!currentDescriptor && createdByDescriptor === currentDescriptor);
|
|
54
|
+
}, [isInstanceMode, currentInstance, currentUser]);
|
|
55
|
+
|
|
56
|
+
const canReorder = React.useMemo(() => {
|
|
57
|
+
if (!isInstanceMode) return true; // Personal Board Mode -> can reorder
|
|
58
|
+
const currentId = currentUser?.id;
|
|
59
|
+
const currentDescriptor = currentUser?.descriptor;
|
|
60
|
+
const isOwner =
|
|
61
|
+
currentInstance.createdBy.id === currentId ||
|
|
62
|
+
(!!currentDescriptor && currentInstance.createdBy?.descriptor === currentDescriptor);
|
|
63
|
+
|
|
64
|
+
const isInOwners =
|
|
65
|
+
(!!currentId && currentInstance.owners.includes(currentId)) ||
|
|
66
|
+
(!!currentDescriptor && currentInstance.owners.includes(currentDescriptor));
|
|
67
|
+
|
|
68
|
+
return isInOwners || isOwner;
|
|
69
|
+
}, [isInstanceMode, currentInstance, currentUser]);
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
isInstanceMode,
|
|
73
|
+
canEditInstance, //roles.isAdmin || roles.isJM || isInstanceOwner,
|
|
74
|
+
canReorder,
|
|
75
|
+
currentUser,
|
|
76
|
+
loading: roles.loading,
|
|
77
|
+
groups: roles.groups,
|
|
78
|
+
/*
|
|
79
|
+
-----------------------------------------------------------------------------------------
|
|
80
|
+
@IMPORTANT: as SPM-HUB does not have public api and endpoint to verify the permissions,
|
|
81
|
+
we are going to bypass the permissions for now (meeting: 28.11.2025 - w/Luca)
|
|
82
|
+
-----------------------------------------------------------------------------------------
|
|
83
|
+
*/
|
|
84
|
+
isAdmin: true,//roles.isAdmin,
|
|
85
|
+
isJM: true, // roles.isJM,
|
|
86
|
+
canCreateInstance: true, //roles.isAdmin || roles.isJM
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#region Private Functions
|
|
91
|
+
async function resolveUserRoles(user: any) {
|
|
92
|
+
try {
|
|
93
|
+
const descriptor = user.descriptor;
|
|
94
|
+
if (!descriptor) {
|
|
95
|
+
return { isAdmin: false, isJM: false, groups: [] as string[] };
|
|
96
|
+
}
|
|
97
|
+
const gClient = graphClient();
|
|
98
|
+
const memberships = await gClient.listMemberships(descriptor);
|
|
99
|
+
|
|
100
|
+
const roles = {
|
|
101
|
+
isAdmin: false,
|
|
102
|
+
isJM: false,
|
|
103
|
+
groups: [] as string[],
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
for (const m of memberships) {
|
|
107
|
+
const group = await gClient.getGroup(m.containerDescriptor) as GraphGroup;
|
|
108
|
+
const name = group?.displayName?.toLowerCase() ?? "";
|
|
109
|
+
|
|
110
|
+
roles.groups.push(name);
|
|
111
|
+
|
|
112
|
+
if (name.includes("project administrators") || name.includes("collection administrators")) {
|
|
113
|
+
roles.isAdmin = true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (name.includes("jm") || name.includes("owner")) {
|
|
117
|
+
roles.isJM = true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return roles;
|
|
122
|
+
} catch (err) {
|
|
123
|
+
console.warn("resolveUserRoles error:", err);
|
|
124
|
+
return { isAdmin: false, isJM: false, groups: [] };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { useInstanceStore } from "../useInstanceStore";
|
|
3
|
+
|
|
4
|
+
describe("useInstanceStore", () => {
|
|
5
|
+
//#region currentInstance
|
|
6
|
+
it("currentInstance: has correct initial state", () => {
|
|
7
|
+
expect(useInstanceStore.getState().currentInstance).toEqual(null);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("setCurrentInstance: sets instance as current iteration.", () => {
|
|
11
|
+
const instance = {
|
|
12
|
+
id: "1",
|
|
13
|
+
name: "Instance Name",
|
|
14
|
+
org: "demo-org",
|
|
15
|
+
createdBy: { id: "u1" },
|
|
16
|
+
owners: [],
|
|
17
|
+
projectTeamPairs: [],
|
|
18
|
+
createdAt: "2025-01-01T00:00:00.000Z",
|
|
19
|
+
updatedAt: "2025-01-01T00:00:00.000Z"
|
|
20
|
+
};
|
|
21
|
+
useInstanceStore.getState().setCurrentInstance(instance);
|
|
22
|
+
expect(useInstanceStore.getState().currentInstance).toEqual(instance);
|
|
23
|
+
});
|
|
24
|
+
//#endregion
|
|
25
|
+
});
|
package/@danieli-automation/devops-plugin-features/src/features/instances/stores/useInstanceStore.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CrossSprintInstanceType } from "features/instances/types/CrossSprintInstanceType";
|
|
2
|
+
import { InstanceStoreType } from "features/instances/stores/types/InstanceStoreType";
|
|
3
|
+
import { create } from "zustand";
|
|
4
|
+
|
|
5
|
+
export const useInstanceStore = create<InstanceStoreType>((set) => ({
|
|
6
|
+
currentInstance: null,
|
|
7
|
+
|
|
8
|
+
setCurrentInstance: (instance: CrossSprintInstanceType | null) => set({ currentInstance: instance }),
|
|
9
|
+
}));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SelectedProjectType } from "features/teams/types/SelectedProjectType";
|
|
2
|
+
|
|
3
|
+
export type CrossSprintInstanceType = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
|
|
8
|
+
org: string;
|
|
9
|
+
createdBy: any;
|
|
10
|
+
owners: string[];
|
|
11
|
+
|
|
12
|
+
projectTeamPairs: SelectedProjectType[];
|
|
13
|
+
|
|
14
|
+
createdAt: string;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
|
|
17
|
+
isDefault?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type CrossSprintInstanceMap = Record<string, CrossSprintInstanceType>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { CrossSprintInstanceType } from "features/instances/types/CrossSprintInstanceType";
|
|
2
|
+
import { TeamMember } from "features/teams/types/TeamMemberType";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Checks whether a user is owner-level for the instance (owner list or creator).
|
|
6
|
+
*
|
|
7
|
+
* @param inst Cross-sprint instance.
|
|
8
|
+
* @param user Current user.
|
|
9
|
+
* @returns `true` when user is owner or creator.
|
|
10
|
+
*/
|
|
11
|
+
export function isInstanceOwner(inst: CrossSprintInstanceType, user: TeamMember) {
|
|
12
|
+
const uid = user?.id;
|
|
13
|
+
const descriptor = user?.descriptor;
|
|
14
|
+
if (!uid && !descriptor) return false;
|
|
15
|
+
|
|
16
|
+
const inOwners = (!!uid && inst.owners?.includes(uid)) || (!!descriptor && inst.owners?.includes(descriptor));
|
|
17
|
+
|
|
18
|
+
return inOwners || isInstanceCreator(inst, user);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Checks whether an instance is explicitly shared with a user but not created by them.
|
|
23
|
+
*
|
|
24
|
+
* @param inst Cross-sprint instance.
|
|
25
|
+
* @param user Current user.
|
|
26
|
+
* @returns `true` when user is in owners list and is not creator.
|
|
27
|
+
*/
|
|
28
|
+
export function isInstanceSharedWithMe(inst: CrossSprintInstanceType, user: TeamMember) {
|
|
29
|
+
const uid = user?.id;
|
|
30
|
+
const descriptor = user?.descriptor;
|
|
31
|
+
if (!uid && !descriptor) return false;
|
|
32
|
+
|
|
33
|
+
const inOwners =
|
|
34
|
+
(!!uid && inst.owners?.includes(uid)) ||
|
|
35
|
+
(!!descriptor && inst.owners?.includes(descriptor));
|
|
36
|
+
|
|
37
|
+
return inOwners && !isInstanceCreator(inst, user);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Checks whether a user is the creator of the given instance.
|
|
42
|
+
*
|
|
43
|
+
* @param inst Cross-sprint instance.
|
|
44
|
+
* @param user Current user.
|
|
45
|
+
* @returns `true` when created-by identity matches user id or descriptor.
|
|
46
|
+
*/
|
|
47
|
+
export function isInstanceCreator(inst: CrossSprintInstanceType, user: TeamMember) {
|
|
48
|
+
const uid = user?.id;
|
|
49
|
+
const descriptor = user?.descriptor;
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
(!!uid && inst.createdBy?.id === uid) ||
|
|
53
|
+
(!!descriptor && inst.createdBy?.descriptor === descriptor)
|
|
54
|
+
);
|
|
55
|
+
}
|