diff --git a/gradle/changelog/hal_embedded_type.yaml b/gradle/changelog/hal_embedded_type.yaml new file mode 100644 index 0000000000..0745b6ae66 --- /dev/null +++ b/gradle/changelog/hal_embedded_type.yaml @@ -0,0 +1,2 @@ +- type: Fixed + description: Fix HalRepresentationWithEmbedded type ([#1793](https://github.com/scm-manager/scm-manager/pull/1793)) diff --git a/scm-plugins/scm-git-plugin/src/main/js/CloneInformation.tsx b/scm-plugins/scm-git-plugin/src/main/js/CloneInformation.tsx index 60a58e852c..98b03d9a65 100644 --- a/scm-plugins/scm-git-plugin/src/main/js/CloneInformation.tsx +++ b/scm-plugins/scm-git-plugin/src/main/js/CloneInformation.tsx @@ -51,7 +51,7 @@ const CloneInformation: FC = ({ url, repository }) => { const error = changesetsError || defaultBranchError; const branch = defaultBranchData?.defaultBranch; - const emptyRepository = changesets?._embedded.changesets.length === 0; + const emptyRepository = (changesets?._embedded?.changesets.length || 0) === 0; return (
diff --git a/scm-plugins/scm-hg-plugin/src/main/js/ProtocolInformation.tsx b/scm-plugins/scm-hg-plugin/src/main/js/ProtocolInformation.tsx index 360dad1d2b..15b4581646 100644 --- a/scm-plugins/scm-hg-plugin/src/main/js/ProtocolInformation.tsx +++ b/scm-plugins/scm-hg-plugin/src/main/js/ProtocolInformation.tsx @@ -44,10 +44,10 @@ const ProtocolInformation: FC = ({ repository }) => { return ; } - const emptyRepository = data?._embedded.changesets.length === 0; + const emptyRepository = (data?._embedded?.changesets.length || 0) === 0; return ( -
+

{t("scm-hg-plugin.information.clone")}

diff --git a/scm-plugins/scm-svn-plugin/src/main/js/ProtocolInformation.tsx b/scm-plugins/scm-svn-plugin/src/main/js/ProtocolInformation.tsx
index b232fc3775..11d3a5e770 100644
--- a/scm-plugins/scm-svn-plugin/src/main/js/ProtocolInformation.tsx
+++ b/scm-plugins/scm-svn-plugin/src/main/js/ProtocolInformation.tsx
@@ -38,7 +38,7 @@ class ProtocolInformation extends React.Component {
       return null;
     }
     return (
-      
+

{t("scm-svn-plugin.information.checkout")}

           svn checkout {href}
diff --git a/scm-ui/ui-api/src/changesets.test.ts b/scm-ui/ui-api/src/changesets.test.ts
index f29cce6a16..c14d41c050 100644
--- a/scm-ui/ui-api/src/changesets.test.ts
+++ b/scm-ui/ui-api/src/changesets.test.ts
@@ -35,9 +35,9 @@ describe("Test changeset hooks", () => {
     type: "hg",
     _links: {
       changesets: {
-        href: "/r/c"
-      }
-    }
+        href: "/r/c",
+      },
+    },
   };
 
   const develop: Branch = {
@@ -45,9 +45,9 @@ describe("Test changeset hooks", () => {
     revision: "42",
     _links: {
       history: {
-        href: "/r/b/c"
-      }
-    }
+        href: "/r/b/c",
+      },
+    },
   };
 
   const changeset: Changeset = {
@@ -55,23 +55,23 @@ describe("Test changeset hooks", () => {
     description: "Awesome change",
     date: new Date(),
     author: {
-      name: "Arthur Dent"
+      name: "Arthur Dent",
     },
     _embedded: {},
-    _links: {}
+    _links: {},
   };
 
   const changesets: ChangesetCollection = {
     page: 1,
     pageTotal: 1,
     _embedded: {
-      changesets: [changeset]
+      changesets: [changeset],
     },
-    _links: {}
+    _links: {},
   };
 
   const expectChangesetCollection = (result?: ChangesetCollection) => {
-    expect(result?._embedded.changesets[0].id).toBe(changesets._embedded.changesets[0].id);
+    expect(result?._embedded?.changesets[0].id).toBe(changesets._embedded?.changesets[0].id);
   };
 
   afterEach(() => {
@@ -85,7 +85,7 @@ describe("Test changeset hooks", () => {
       const queryClient = createInfiniteCachingClient();
 
       const { result, waitFor } = renderHook(() => useChangesets(repository), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
 
       await waitFor(() => {
@@ -98,14 +98,14 @@ describe("Test changeset hooks", () => {
     it("should return changesets for page", async () => {
       fetchMock.getOnce("/api/v2/r/c", changesets, {
         query: {
-          page: 42
-        }
+          page: 42,
+        },
       });
 
       const queryClient = createInfiniteCachingClient();
 
       const { result, waitFor } = renderHook(() => useChangesets(repository, { page: 42 }), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
 
       await waitFor(() => {
@@ -121,7 +121,7 @@ describe("Test changeset hooks", () => {
       const queryClient = createInfiniteCachingClient();
 
       const { result, waitFor } = renderHook(() => useChangesets(repository, { branch: develop }), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
 
       await waitFor(() => {
@@ -137,7 +137,7 @@ describe("Test changeset hooks", () => {
       const queryClient = createInfiniteCachingClient();
 
       const { result, waitFor } = renderHook(() => useChangesets(repository), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
 
       await waitFor(() => {
@@ -149,7 +149,7 @@ describe("Test changeset hooks", () => {
         "hitchhiker",
         "heart-of-gold",
         "changeset",
-        "42"
+        "42",
       ]);
 
       expect(changeset?.id).toBe("42");
@@ -163,7 +163,7 @@ describe("Test changeset hooks", () => {
       const queryClient = createInfiniteCachingClient();
 
       const { result, waitFor } = renderHook(() => useChangeset(repository, "42"), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
 
       await waitFor(() => {
diff --git a/scm-ui/ui-api/src/changesets.ts b/scm-ui/ui-api/src/changesets.ts
index b10854acb4..c9b27517b2 100644
--- a/scm-ui/ui-api/src/changesets.ts
+++ b/scm-ui/ui-api/src/changesets.ts
@@ -67,7 +67,7 @@ export const useChangesets = (
   const key = branchQueryKey(repository, branch, "changesets", request?.page || 0);
   return useQuery(key, () => apiClient.get(link).then((response) => response.json()), {
     onSuccess: (changesetCollection) => {
-      changesetCollection._embedded.changesets.forEach((changeset) => {
+      changesetCollection._embedded?.changesets.forEach((changeset) => {
         queryClient.setQueryData(changesetQueryKey(repository, changeset.id), changeset);
       });
     },
diff --git a/scm-ui/ui-api/src/history.ts b/scm-ui/ui-api/src/history.ts
index 1a460fd47e..94d6467ad5 100644
--- a/scm-ui/ui-api/src/history.ts
+++ b/scm-ui/ui-api/src/history.ts
@@ -53,7 +53,7 @@ export const useHistory = (
     {
       keepPreviousData: true,
       onSuccess: (changesets: ChangesetCollection) => {
-        changesets._embedded.changesets.forEach((changeset: Changeset) =>
+        changesets._embedded?.changesets.forEach((changeset: Changeset) =>
           queryClient.setQueryData(changesetQueryKey(repository, changeset.id), changeset)
         );
       },
diff --git a/scm-ui/ui-api/src/notifications.ts b/scm-ui/ui-api/src/notifications.ts
index e5ea5a9a4a..5b24b80202 100644
--- a/scm-ui/ui-api/src/notifications.ts
+++ b/scm-ui/ui-api/src/notifications.ts
@@ -34,21 +34,21 @@ export const useNotifications = () => {
   const link = (me?._links["notifications"] as Link)?.href;
   const { data, error, isLoading, refetch } = useQuery(
     "notifications",
-    () => apiClient.get(link).then(response => response.json()),
+    () => apiClient.get(link).then((response) => response.json()),
     {
-      enabled: !!link
+      enabled: !!link,
     }
   );
 
   const memoizedRefetch = useCallback(() => {
-    return refetch().then(r => r.data);
+    return refetch().then((r) => r.data);
   }, [refetch]);
 
   return {
     data,
     error,
     isLoading,
-    refetch: memoizedRefetch
+    refetch: memoizedRefetch,
   };
 };
 
@@ -58,13 +58,13 @@ export const useDismissNotification = (notification: Notification) => {
   const { data, isLoading, error, mutate } = useMutation(() => apiClient.delete(link), {
     onSuccess: () => {
       queryClient.invalidateQueries("notifications");
-    }
+    },
   });
   return {
     isLoading,
     error,
     dismiss: () => mutate(),
-    isCleared: !!data
+    isCleared: !!data,
   };
 };
 
@@ -74,13 +74,13 @@ export const useClearNotifications = (notificationCollection: NotificationCollec
   const { data, isLoading, error, mutate } = useMutation(() => apiClient.delete(link), {
     onSuccess: () => {
       queryClient.invalidateQueries("notifications");
-    }
+    },
   });
   return {
     isLoading,
     error,
     clear: () => mutate(),
-    isCleared: !!data
+    isCleared: !!data,
   };
 };
 
@@ -99,13 +99,13 @@ export const useNotificationSubscription = (
   const onVisible = useCallback(() => {
     // we don't need to catch the error,
     // because if the refetch throws an error the parent useNotifications should catch it
-    refetch().then(collection => {
+    refetch().then((collection) => {
       if (collection) {
-        const newNotifications = collection._embedded.notifications.filter(n => {
+        const newNotifications = collection._embedded?.notifications.filter((n) => {
           return disconnectedAt && disconnectedAt < new Date(n.createdAt);
         });
-        if (newNotifications.length > 0) {
-          setNotifications(previous => [...previous, ...newNotifications]);
+        if (newNotifications && newNotifications.length > 0) {
+          setNotifications((previous) => [...previous, ...newNotifications]);
         }
         setDisconnectedAt(undefined);
       }
@@ -118,7 +118,7 @@ export const useNotificationSubscription = (
 
   const received = useCallback(
     (notification: Notification) => {
-      setNotifications(previous => [...previous, notification]);
+      setNotifications((previous) => [...previous, notification]);
       refetch();
     },
     [refetch]
@@ -137,9 +137,9 @@ export const useNotificationSubscription = (
       const connect = () => {
         disconnect();
         cancel = apiClient.subscribe(link, {
-          notification: event => {
+          notification: (event) => {
             received(JSON.parse(event.data));
-          }
+          },
         });
       };
 
@@ -166,7 +166,7 @@ export const useNotificationSubscription = (
 
   const remove = useCallback(
     (notification: Notification) => {
-      setNotifications(oldNotifications => [...oldNotifications.filter(n => !isEqual(n, notification))]);
+      setNotifications((oldNotifications) => [...oldNotifications.filter((n) => !isEqual(n, notification))]);
     },
     [setNotifications]
   );
@@ -178,6 +178,6 @@ export const useNotificationSubscription = (
   return {
     notifications,
     remove,
-    clear
+    clear,
   };
 };
diff --git a/scm-ui/ui-api/src/permissions.test.ts b/scm-ui/ui-api/src/permissions.test.ts
index 91cfa13067..63695b4cdd 100644
--- a/scm-ui/ui-api/src/permissions.test.ts
+++ b/scm-ui/ui-api/src/permissions.test.ts
@@ -30,7 +30,7 @@ import {
   Repository,
   RepositoryRole,
   RepositoryRoleCollection,
-  RepositoryVerbs
+  RepositoryVerbs,
 } from "@scm-manager/ui-types";
 import fetchMock from "fetch-mock-jest";
 import { renderHook } from "@testing-library/react-hooks";
@@ -41,7 +41,7 @@ import {
   useDeletePermission,
   usePermissions,
   useRepositoryVerbs,
-  useUpdatePermission
+  useUpdatePermission,
 } from "./permissions";
 import { act } from "react-test-renderer";
 
@@ -49,21 +49,21 @@ describe("permission hooks test", () => {
   const readRole: RepositoryRole = {
     name: "READ",
     verbs: ["read", "pull"],
-    _links: {}
+    _links: {},
   };
 
   const roleCollection: RepositoryRoleCollection = {
     _embedded: {
-      repositoryRoles: [readRole]
+      repositoryRoles: [readRole],
     },
     _links: {},
     page: 1,
-    pageTotal: 1
+    pageTotal: 1,
   };
 
   const verbCollection: RepositoryVerbs = {
     verbs: ["read", "pull"],
-    _links: {}
+    _links: {},
   };
 
   const readPermission: Permission = {
@@ -73,9 +73,9 @@ describe("permission hooks test", () => {
     groupPermission: false,
     _links: {
       update: {
-        href: "/p/trillian"
-      }
-    }
+        href: "/p/trillian",
+      },
+    },
   };
 
   const writePermission: Permission = {
@@ -85,32 +85,32 @@ describe("permission hooks test", () => {
     groupPermission: false,
     _links: {
       delete: {
-        href: "/p/dent"
-      }
-    }
+        href: "/p/dent",
+      },
+    },
   };
 
   const permissionsRead: PermissionCollection = {
     _embedded: {
-      permissions: [readPermission]
+      permissions: [readPermission],
     },
-    _links: {}
+    _links: {},
   };
 
   const permissionsWrite: PermissionCollection = {
     _embedded: {
-      permissions: [writePermission]
+      permissions: [writePermission],
     },
-    _links: {}
+    _links: {},
   };
 
   const namespace: Namespace = {
     namespace: "spaceships",
     _links: {
       permissions: {
-        href: "/ns/spaceships/permissions"
-      }
-    }
+        href: "/ns/spaceships/permissions",
+      },
+    },
   };
 
   const repository: Repository = {
@@ -119,9 +119,9 @@ describe("permission hooks test", () => {
     type: "git",
     _links: {
       permissions: {
-        href: "/r/heart-of-gold/permissions"
-      }
-    }
+        href: "/r/heart-of-gold/permissions",
+      },
+    },
   };
 
   const queryClient = createInfiniteCachingClient();
@@ -137,7 +137,7 @@ describe("permission hooks test", () => {
       fetchMock.get("/api/v2/verbs", verbCollection);
 
       const { result, waitFor } = renderHook(() => useRepositoryVerbs(), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
       await waitFor(() => {
         return !!result.current.data;
@@ -152,23 +152,23 @@ describe("permission hooks test", () => {
         version: "x.y.z",
         _links: {
           repositoryRoles: {
-            href: "/roles"
+            href: "/roles",
           },
           repositoryVerbs: {
-            href: "/verbs"
-          }
-        }
+            href: "/verbs",
+          },
+        },
       });
       fetchMock.get("/api/v2/roles", roleCollection);
       fetchMock.get("/api/v2/verbs", verbCollection);
 
       const { result, waitFor } = renderHook(() => useAvailablePermissions(), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
       await waitFor(() => {
         return !!result.current.data;
       });
-      expect(result.current.data?.repositoryRoles).toEqual(roleCollection._embedded.repositoryRoles);
+      expect(result.current.data?.repositoryRoles).toEqual(roleCollection._embedded?.repositoryRoles);
       expect(result.current.data?.repositoryVerbs).toEqual(verbCollection.verbs);
     });
   });
@@ -176,7 +176,7 @@ describe("permission hooks test", () => {
   describe("usePermissions tests", () => {
     const fetchPermissions = async (namespaceOrRepository: Namespace | Repository) => {
       const { result, waitFor } = renderHook(() => usePermissions(namespaceOrRepository), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
       await waitFor(() => {
         return !!result.current.data;
@@ -216,14 +216,14 @@ describe("permission hooks test", () => {
       fetchMock.postOnce("/api/v2/ns/spaceships/permissions", {
         status: 201,
         headers: {
-          Location: "/ns/spaceships/permissions/42"
-        }
+          Location: "/ns/spaceships/permissions/42",
+        },
       });
 
       fetchMock.getOnce("/api/v2/ns/spaceships/permissions/42", readPermission);
 
       const { result, waitForNextUpdate } = renderHook(() => useCreatePermission(namespace), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
 
       await act(() => {
@@ -241,11 +241,11 @@ describe("permission hooks test", () => {
 
     it("should fail without location header", async () => {
       fetchMock.postOnce("/api/v2/ns/spaceships/permissions", {
-        status: 201
+        status: 201,
       });
 
       const { result, waitForNextUpdate } = renderHook(() => useCreatePermission(namespace), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
 
       await act(() => {
@@ -270,11 +270,11 @@ describe("permission hooks test", () => {
   describe("useDeletePermission tests", () => {
     const deletePermission = async () => {
       fetchMock.deleteOnce("/api/v2/p/dent", {
-        status: 204
+        status: 204,
       });
 
       const { result, waitForNextUpdate } = renderHook(() => useDeletePermission(repository), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
 
       await act(() => {
@@ -308,11 +308,11 @@ describe("permission hooks test", () => {
   describe("useUpdatePermission tests", () => {
     const updatePermission = async () => {
       fetchMock.putOnce("/api/v2/p/trillian", {
-        status: 204
+        status: 204,
       });
 
       const { result, waitForNextUpdate } = renderHook(() => useUpdatePermission(repository), {
-        wrapper: createWrapper(undefined, queryClient)
+        wrapper: createWrapper(undefined, queryClient),
       });
 
       await act(() => {
diff --git a/scm-ui/ui-api/src/permissions.ts b/scm-ui/ui-api/src/permissions.ts
index 1ab7ea46f3..f02fe6d93f 100644
--- a/scm-ui/ui-api/src/permissions.ts
+++ b/scm-ui/ui-api/src/permissions.ts
@@ -53,7 +53,7 @@ export const useAvailablePermissions = () => {
   if (roles.data && verbs.data) {
     data = {
       repositoryVerbs: verbs.data.verbs,
-      repositoryRoles: roles.data._embedded.repositoryRoles,
+      repositoryRoles: roles.data._embedded?.repositoryRoles || [],
     };
   }
 
diff --git a/scm-ui/ui-api/src/repositories.test.ts b/scm-ui/ui-api/src/repositories.test.ts
index c262f5586c..e9aebe43c8 100644
--- a/scm-ui/ui-api/src/repositories.test.ts
+++ b/scm-ui/ui-api/src/repositories.test.ts
@@ -308,7 +308,7 @@ describe("Test repository hooks", () => {
       });
       expect(result.current.data).toBeDefined();
       if (result.current?.data) {
-        expect(result.current?.data._embedded.repositoryTypes[0].name).toEqual("git");
+        expect(result.current?.data._embedded?.repositoryTypes[0].name).toEqual("git");
       }
     });
   });
diff --git a/scm-ui/ui-api/src/repositories.ts b/scm-ui/ui-api/src/repositories.ts
index 64076629dd..31ad3fa6ee 100644
--- a/scm-ui/ui-api/src/repositories.ts
+++ b/scm-ui/ui-api/src/repositories.ts
@@ -71,7 +71,7 @@ export const useRepositories = (request?: UseRepositoriesRequest): ApiResult {
         // prepare single repository cache
-        repositories._embedded.repositories.forEach((repository: Repository) => {
+        repositories._embedded?.repositories.forEach((repository: Repository) => {
           queryClient.setQueryData(["repository", repository.namespace, repository.name], repository);
         });
       },
@@ -363,7 +363,7 @@ export const useRenameRepository = (repository: Repository) => {
   const { mutate, isLoading, error, data } = useMutation(
     ({ name, namespace }) => apiClient.post(url, { namespace, name }, "application/vnd.scmm-repository+json;v=2"),
     {
-      onSuccess: () => queryClient.removeQueries(repoQueryKey(repository))
+      onSuccess: () => queryClient.removeQueries(repoQueryKey(repository)),
     }
   );
 
diff --git a/scm-ui/ui-api/src/repository-roles.ts b/scm-ui/ui-api/src/repository-roles.ts
index 117546e23f..85bb4a2c6a 100644
--- a/scm-ui/ui-api/src/repository-roles.ts
+++ b/scm-ui/ui-api/src/repository-roles.ts
@@ -43,13 +43,13 @@ export const useRepositoryRoles = (request?: UseRepositoryRolesRequest): ApiResu
 
   return useQuery(
     ["repositoryRoles", request?.page || 0],
-    () => apiClient.get(`${indexLink}?${createQueryString(queryParams)}`).then(response => response.json()),
+    () => apiClient.get(`${indexLink}?${createQueryString(queryParams)}`).then((response) => response.json()),
     {
       onSuccess: (repositoryRoles: RepositoryRoleCollection) => {
-        repositoryRoles._embedded.repositoryRoles.forEach((repositoryRole: RepositoryRole) =>
+        repositoryRoles._embedded?.repositoryRoles.forEach((repositoryRole: RepositoryRole) =>
           queryClient.setQueryData(["repositoryRole", repositoryRole.name], repositoryRole)
         );
-      }
+      },
     }
   );
 };
@@ -57,7 +57,7 @@ export const useRepositoryRoles = (request?: UseRepositoryRolesRequest): ApiResu
 export const useRepositoryRole = (name: string): ApiResult => {
   const indexLink = useRequiredIndexLink("repositoryRoles");
   return useQuery(["repositoryRole", name], () =>
-    apiClient.get(urls.concat(indexLink, name)).then(response => response.json())
+    apiClient.get(urls.concat(indexLink, name)).then((response) => response.json())
   );
 };
 
@@ -65,14 +65,14 @@ const createRepositoryRole = (link: string) => {
   return (repositoryRole: RepositoryRoleCreation) => {
     return apiClient
       .post(link, repositoryRole, "application/vnd.scmm-repositoryRole+json;v=2")
-      .then(response => {
+      .then((response) => {
         const location = response.headers.get("Location");
         if (!location) {
           throw new Error("Server does not return required Location header");
         }
         return apiClient.get(location);
       })
-      .then(response => response.json());
+      .then((response) => response.json());
   };
 };
 
@@ -82,24 +82,24 @@ export const useCreateRepositoryRole = () => {
   const { mutate, data, isLoading, error } = useMutation(
     createRepositoryRole(link),
     {
-      onSuccess: repositoryRole => {
+      onSuccess: (repositoryRole) => {
         queryClient.setQueryData(["repositoryRole", repositoryRole.name], repositoryRole);
         return queryClient.invalidateQueries(["repositoryRoles"]);
-      }
+      },
     }
   );
   return {
     create: (repositoryRole: RepositoryRoleCreation) => mutate(repositoryRole),
     isLoading,
     error,
-    repositoryRole: data
+    repositoryRole: data,
   };
 };
 
 export const useUpdateRepositoryRole = () => {
   const queryClient = useQueryClient();
   const { mutate, isLoading, error, data } = useMutation(
-    repositoryRole => {
+    (repositoryRole) => {
       const updateUrl = requiredLink(repositoryRole, "update");
       return apiClient.put(updateUrl, repositoryRole, "application/vnd.scmm-repositoryRole+json;v=2");
     },
@@ -107,21 +107,21 @@ export const useUpdateRepositoryRole = () => {
       onSuccess: async (_, repositoryRole) => {
         await queryClient.invalidateQueries(["repositoryRole", repositoryRole.name]);
         await queryClient.invalidateQueries(["repositoryRoles"]);
-      }
+      },
     }
   );
   return {
     update: (repositoryRole: RepositoryRole) => mutate(repositoryRole),
     isLoading,
     error,
-    isUpdated: !!data
+    isUpdated: !!data,
   };
 };
 
 export const useDeleteRepositoryRole = () => {
   const queryClient = useQueryClient();
   const { mutate, isLoading, error, data } = useMutation(
-    repositoryRole => {
+    (repositoryRole) => {
       const deleteUrl = requiredLink(repositoryRole, "delete");
       return apiClient.delete(deleteUrl);
     },
@@ -129,13 +129,13 @@ export const useDeleteRepositoryRole = () => {
       onSuccess: async (_, name) => {
         await queryClient.invalidateQueries(["repositoryRole", name]);
         await queryClient.invalidateQueries(["repositoryRoles"]);
-      }
+      },
     }
   );
   return {
     remove: (repositoryRole: RepositoryRole) => mutate(repositoryRole),
     isLoading,
     error,
-    isDeleted: !!data
+    isDeleted: !!data,
   };
 };
diff --git a/scm-ui/ui-api/src/users.ts b/scm-ui/ui-api/src/users.ts
index c3202a3c4e..b69350371b 100644
--- a/scm-ui/ui-api/src/users.ts
+++ b/scm-ui/ui-api/src/users.ts
@@ -24,7 +24,7 @@
 
 import { ApiResult, useRequiredIndexLink } from "./base";
 import { useMutation, useQuery, useQueryClient } from "react-query";
-import {Link, Me, User, UserCollection, UserCreation} from "@scm-manager/ui-types";
+import { Link, Me, User, UserCollection, UserCreation } from "@scm-manager/ui-types";
 import { apiClient } from "./apiclient";
 import { createQueryString } from "./utils";
 import { concat } from "./urls";
@@ -52,7 +52,7 @@ export const useUsers = (request?: UseUsersRequest): ApiResult =
     () => apiClient.get(`${indexLink}?${createQueryString(queryParams)}`).then((response) => response.json()),
     {
       onSuccess: (users: UserCollection) => {
-        users._embedded.users.forEach((user: User) => queryClient.setQueryData(["user", user.name], user));
+        users._embedded?.users.forEach((user: User) => queryClient.setQueryData(["user", user.name], user));
       },
     }
   );
@@ -215,7 +215,7 @@ export const useSetUserPassword = (user: User) => {
     passwordOverwritten: !!data,
     isLoading,
     error,
-    reset
+    reset,
   };
 };
 
@@ -235,6 +235,6 @@ export const useChangeUserPassword = (user: User | Me) => {
     passwordChanged: !!data,
     isLoading,
     error,
-    reset
+    reset,
   };
 };
diff --git a/scm-ui/ui-components/src/repos/changesets/ChangesetTags.tsx b/scm-ui/ui-components/src/repos/changesets/ChangesetTags.tsx
index 761ff1dd89..5b1afd0eaf 100644
--- a/scm-ui/ui-components/src/repos/changesets/ChangesetTags.tsx
+++ b/scm-ui/ui-components/src/repos/changesets/ChangesetTags.tsx
@@ -33,7 +33,7 @@ type Props = {
 class ChangesetTags extends React.Component {
   getTags = () => {
     const { changeset } = this.props;
-    return changeset._embedded.tags || [];
+    return changeset._embedded?.tags || [];
   };
 
   render() {
diff --git a/scm-ui/ui-types/src/hal.ts b/scm-ui/ui-types/src/hal.ts
index 04e4b66fb1..abc843a2e5 100644
--- a/scm-ui/ui-types/src/hal.ts
+++ b/scm-ui/ui-types/src/hal.ts
@@ -44,7 +44,7 @@ export type HalRepresentation = {
 };
 
 export type HalRepresentationWithEmbedded = HalRepresentation & {
-  _embedded: T;
+  _embedded?: T;
 };
 
 export type PagedCollection = HalRepresentationWithEmbedded & {