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

fix: update org codelist controller #14667

Merged
merged 3 commits into from
Feb 14, 2025

Conversation

Konrad-Simso
Copy link
Contributor

@Konrad-Simso Konrad-Simso commented Feb 14, 2025

Description

Update OrgCodeListController and OrgCodeListService to find the correct repository.

Related Issue(s)

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)

Summary by CodeRabbit

  • New Features

    • Added organization-level code list management so users can create, update, upload, and delete code lists directly within the dashboard.
    • Introduced dynamic navigation that smoothly transitions between app views and organizational libraries.
    • Integrated localized labels and alerts to guide users when accessing organization-specific content.
  • Enhancements

    • Improved file upload handling and routing for options retrieval.
    • Streamlined text resource management for a more responsive and reliable dashboard experience.

Copy link
Contributor

coderabbitai bot commented Feb 14, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This pull request introduces functionality for managing organization-level code lists and options. A new controller and corresponding service have been added to handle CRUD operations for code lists. Existing controllers, repositories, and factory methods are updated to support dynamic folder paths for retrieving options. In addition, interface additions, dependency registrations, and extensive updates to backend tests and frontend components (including hooks, UI routing, and styles) ensure that code list operations are integrated consistently across the application and its shared packages.

Changes

Files Change Summary
backend/.../Controllers/Organisation/OrgCodeListController.cs
backend/.../Controllers/Preview/(InstancesController.cs, OldInstancesController.cs, PreviewController.cs)
Added new OrgCodeListController with methods (Get, Create, Update, Upload, Delete) for organization code list management; updated preview controllers to include a new OptionsFolderPath parameter.
backend/.../Factories/AltinnGitRepositoryFactory.cs
backend/.../Infrastructure/GitRepository/(AltinnAppGitRepository.cs, AltinnOrgGitRepository.cs)
backend/.../Infrastructure/ServiceRegistration.cs
backend/.../Services/(Implementation/OptionsService.cs, Implementation/Organisation/OrgCodeListService.cs, Interfaces/(IAltinnGitRepositoryFactory.cs, Organisation/IOrgCodeListService.cs))
Introduced org‑specific repository and service layers with new methods for managing code lists; updated interfaces, factory methods, and registered the new IOrgCodeListService in dependency injection.
backend/.../Tests/(AltinnAppGitRepositoryTests.cs, TextsServiceTest.cs) Updated backend tests to pass the new optionsFolderPath parameter to modified method signatures in the Git repository classes.
frontend/dashboard/(app/App.test.tsx, App.tsx, context/HeaderContext/*, hooks/(useContextRedirectionGuard.ts, useSubRoute/*), pages/(CreateService/*, Dashboard/*, OrgContentLibrary/*, PageLayout/*)) Restructured dashboard components and tests: integrated subroute-based routing, added new helper hooks, modified header navigation and UI routing, and enhanced test coverage.
frontend/packages/shared/src/(api/(mutations.ts, paths.js, queries.ts), constants.js, hooks/(mutations/*, queries/*), mocks/(codeListsResponse.ts, queriesMock.ts, textResourcesMock.ts), types/(CodeList*.ts, QueryKey.ts, api/CodeListsResponse.ts))
frontend/libs/studio-pure-functions/(src/(FileUtils/*, StringUtils/*))
Added new API endpoints and mutation/query hooks for organization code lists and text resources; expanded mocks and types; introduced utility classes for file and string operations; updated shared constants.

Possibly related PRs

Suggested labels

solution/studio/designer, backend, area/content-library, quality/testing, skip-releasenotes, team/studio-domain1, skip-documentation

Suggested reviewers

  • ErlingHauan
  • github-actions

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added area/dashboard Area: Related to the dashboard application area/app-preview Area: Related to test and preview of apps that are developed in Altinn Studio. solution/studio/designer Issues related to the Altinn Studio Designer solution. quality/testing Tests that are missing, needs to be created or could be improved. backend frontend labels Feb 14, 2025
@Konrad-Simso Konrad-Simso added skip-releasenotes Issues that do not make sense to list in our release notes team/studio-domain1 skip-documentation Issues where updating documentation is not relevant area/content-library Area: Related to library for shared resources and removed area/dashboard Area: Related to the dashboard application area/app-preview Area: Related to test and preview of apps that are developed in Altinn Studio. quality/testing Tests that are missing, needs to be created or could be improved. frontend labels Feb 14, 2025
@Konrad-Simso Konrad-Simso changed the base branch from main to org-library-mvp February 14, 2025 11:52
@Konrad-Simso Konrad-Simso changed the title Fix/update org codelist controller fix: update org codelist controller Feb 14, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

Actionable comments posted: 7

🧹 Nitpick comments (45)
backend/src/Designer/Services/Implementation/OptionsService.cs (1)

23-23: Consider moving the path to configuration.

While the constant is well-named and uses cross-platform compatible path separators, hardcoding the path might make it less flexible for different environments or future changes.

Consider moving this to configuration (e.g., appsettings.json) for better maintainability:

-    private const string OptionsFolderPath = "App/options/";

Add to your configuration class:

public class AppSettings 
{
    public string OptionsFolderPath { get; set; }
}

And inject it via constructor:

private readonly string _optionsFolderPath;

public OptionsService(IOptions<AppSettings> settings, ...)
{
    _optionsFolderPath = settings.Value.OptionsFolderPath;
}
frontend/dashboard/app/App.tsx (3)

25-39: Consider handling additional default or idle states.
React-query can also yield an 'idle' status or additional states. A default case in the switch statement could handle unexpected statuses and log or display an error.

 switch (queryStatus) {
    case 'pending':
      return <PendingPage />;
    case 'error':
      return <ErrorMessage userStatus={userStatus} />;
    case 'success':
      return <AppWithData user={user} organizations={organizations} />;
+   default:
+     console.warn('Unhandled queryStatus:', queryStatus);
+     return <ErrorMessage userStatus={userStatus} />;
 }

74-90: AppWithData routing structure looks good.
The nested routes and page layout are cleanly organized. Consider adding a 404 or fallback route for unknown paths.


92-103: SubrouteGuard missing a default case.
If additional subroutes are introduced later, returning a placeholder or fallback component could enhance user experience.

switch (subrouteWithLeadingSlash) {
  case APP_DASHBOARD_BASENAME:
    return <Dashboard {...props} />;
  case ORG_LIBRARY_BASENAME:
    return <OrgContentLibrary />;
+ default:
+   return <StudioPageError title="Not Found" message="Unknown subroute" />;
}
frontend/dashboard/hooks/guards/useContextRedirectionGuard.ts (1)

41-52: handleContextRedirection approach is concise.
Conditionally redirecting to self or a different context is well-structured. Consider adding logs or telemetry for debugging.

backend/src/Designer/Infrastructure/GitRepository/AltinnOrgGitRepository.cs (1)

95-109: Consider consistency in directory creation for UpdateCodeList.
While update typically expects an existing file, if the folder is missing, this operation may fail. Consider setting createDirectory: true if you want to allow newly added code lists to be updated in all scenarios.

await WriteTextByRelativePathAsync(
  codeListFilePath,
  codeListString,
- false,
+ true,
  cancellationToken
);
backend/src/Designer/Controllers/Organisation/OrgCodeListController.cs (2)

53-56: Consider using 404 status code instead of 204
When no code lists are found, responding with NoContent() might confuse clients expecting a 404 for “not found.” Evaluate whether returning a 404 would more accurately reflect the resource’s absence.

- return NoContent();
+ return NotFound();

127-151: Return consistent status codes on non-existent code list deletes
If a code list does not exist, the method returns HTTP 200 along with a message stating the file has been deleted. This may mislead clients. Consider returning 404 or a specific status to indicate that it did not exist.

if (CodeListExists)
{
   ...
}
else
{
-    return Ok($"The code-list file {codeListId}.json has been deleted.");
+    return NotFound($"No code-list found for {codeListId}.json");
}
backend/src/Designer/Services/Implementation/Organisation/OrgCodeListService.cs (1)

99-99: Handle edge cases for filename parsing
Replacing ".json" in payload.FileName assumes the extension exists exactly once. You might want to validate or handle cases where the file extension is missing or the filename contains multiple occurrences of ".json".

- string codeListId = payload.FileName.Replace(".json", "");
+ string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(payload.FileName);
+ string codeListId = fileNameWithoutExtension ?? "default_code_list";
frontend/packages/shared/src/types/CodeListData.ts (1)

3-7: Add JSDoc comments to document the type.

Consider adding JSDoc comments to document the purpose of each field, especially hasError to clarify when it's set to true.

+/**
+ * Represents a code list with its data and status.
+ */
 export type CodeListData = {
+  /** The title/name of the code list */
   title: string;
+  /** Optional array of code list items */
   data?: CodeListItem[];
+  /** Indicates if there was an error processing the code list */
   hasError?: boolean;
 };
frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.ts (1)

1-7: Convert class to standalone function.

Since this utility contains only a single static method, it would be cleaner to implement it as a standalone function rather than a class.

-export class FileUtils {
-  static convertToFormData = (file: File): FormData => {
-    const formData = new FormData();
-    formData.append('file', file);
-    return formData;
-  };
-}
+/**
+ * Converts a File object to FormData.
+ */
+export const convertToFormData = (file: File): FormData => {
+  const formData = new FormData();
+  formData.append('file', file);
+  return formData;
+};
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-7: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

frontend/dashboard/pages/OrgContentLibrary/utils.ts (1)

3-10: Improve type safety and performance.

Consider these improvements:

  1. Use the enum type directly instead of string
  2. Use a Set for more efficient lookups
  3. Consider positive logic for better readability
-export function isOrg(contextType: string): boolean {
-  const notOrgContexts: string[] = [
+export function isOrg(contextType: SelectedContextType): boolean {
+  const notOrgContexts = new Set<SelectedContextType>([
     SelectedContextType.Self,
     SelectedContextType.All,
     SelectedContextType.None,
-  ];
-  return !notOrgContexts.includes(contextType);
+  ]);
+  return !notOrgContexts.has(contextType);
 }

Alternative with positive logic:

export function isOrg(contextType: SelectedContextType): boolean {
  return contextType !== SelectedContextType.Self &&
         contextType !== SelectedContextType.All &&
         contextType !== SelectedContextType.None;
}
frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.test.ts (2)

13-14: Use appropriate matcher for File object comparison.

The toBe matcher performs strict equality comparison which might not be appropriate for File objects. Consider using toEqual or a custom matcher that compares relevant File properties.

-      expect(retrievedFile).toBe(file);
+      expect(retrievedFile).toEqual(file);

3-17: Enhance test coverage with additional test cases.

Consider adding tests for:

  • Error cases (e.g., null/undefined file)
  • Different file types (e.g., images, PDFs)
  • Large files
  • Files with special characters in names

Would you like me to generate additional test cases to improve coverage?

frontend/dashboard/pages/PageLayout/DashboardHeader/dashboardHeaderMenuItems.ts (2)

8-12: Add JSDoc comments to document the interface.

Consider adding JSDoc comments to document the purpose and usage of the HeaderMenuItem interface and its properties.

+/**
+ * Represents a menu item in the dashboard header navigation.
+ */
 export interface HeaderMenuItem {
+  /** Unique identifier for the menu item */
   key: HeaderMenuItemKey;
+  /** Route path for the menu item */
   link: string;
+  /** Translation key for the menu item's display text */
   name: string;
 }

14-25: Consider making the menu items array readonly.

Since this is a constant array of menu items, consider making it immutable to prevent accidental modifications.

-export const dashboardHeaderMenuItems: HeaderMenuItem[] = [
+export const dashboardHeaderMenuItems: ReadonlyArray<HeaderMenuItem> = [
frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts (1)

12-17: Add error handling to the mutation.

Consider adding error handling to manage failed deletions gracefully.

Apply this diff to add error handling:

   return useMutation({
     mutationFn,
     onSuccess: (newData: CodeListsResponse) => {
       queryClient.setQueryData([QueryKey.OrgCodeLists, org], newData);
     },
+    onError: (error: Error) => {
+      // Handle error (e.g., show error notification)
+      console.error('Failed to delete code list:', error);
+    },
   });
frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.test.ts (1)

7-12: LGTM! Consider adding more test cases.

The test correctly verifies the basic functionality of calling getCodeListsForOrg with the right parameter.

Consider adding test cases for:

  • Error handling
  • Loading state
  • Empty response
  • Response with data
frontend/packages/shared/src/hooks/mutations/useCreateTextResourcesForOrgMutation.ts (1)

6-19: Consider adding error handling and optimistic updates.

The mutation hook is well-structured but could benefit from additional features.

Consider adding:

  1. Error handling with onError callback
  2. Optimistic updates using onMutate
  3. Cache rollback on error using onError

Example implementation:

 export const useCreateTextResourcesForOrgMutation = (org: string, language: string) => {
   const q = useQueryClient();
   const { createTextResourcesForOrg } = useServicesContext();
   return useMutation({
     mutationFn: async () => {
       const textResourcesWithLanguage: ITextResourcesWithLanguage[] =
         await createTextResourcesForOrg(org, language);

       return textResourcesWithLanguage;
     },
     onSuccess: (textResourcesWithLanguage) =>
       q.setQueryData([QueryKey.TextResourcesForOrg, org], textResourcesWithLanguage),
+    onError: (error, _, context) => {
+      // Rollback to previous state on error
+      if (context?.previousData) {
+        q.setQueryData([QueryKey.TextResourcesForOrg, org], context.previousData);
+      }
+    },
+    onMutate: async () => {
+      // Cancel outgoing fetches
+      await q.cancelQueries([QueryKey.TextResourcesForOrg, org]);
+      // Save previous data
+      const previousData = q.getQueryData([QueryKey.TextResourcesForOrg, org]);
+      return { previousData };
+    },
   });
 };
frontend/packages/shared/src/hooks/mutations/useUpdateTextResourcesForOrgMutation.ts (1)

6-19: Consider adding error handling and optimistic updates.

The mutation hook is well-structured but could benefit from additional features. Similar improvements as suggested for useCreateTextResourcesForOrgMutation would be beneficial here.

Consider adding:

  1. Error handling with onError callback
  2. Optimistic updates using onMutate
  3. Cache rollback on error using onError

Example implementation:

 export const useUpdateTextResourcesForOrgMutation = (org: string, language: string) => {
   const q = useQueryClient();
   const { updateTextResourcesForOrg } = useServicesContext();
   return useMutation({
     mutationFn: async (payload: ITextResource[]) => {
       const textResourcesWithLanguage: ITextResourcesWithLanguage[] =
         await updateTextResourcesForOrg(org, language, payload);

       return textResourcesWithLanguage;
     },
     onSuccess: (textResourcesWithLanguage) =>
       q.setQueryData([QueryKey.TextResourcesForOrg, org], textResourcesWithLanguage),
+    onError: (error, _, context) => {
+      // Rollback to previous state on error
+      if (context?.previousData) {
+        q.setQueryData([QueryKey.TextResourcesForOrg, org], context.previousData);
+      }
+    },
+    onMutate: async (payload) => {
+      // Cancel outgoing fetches
+      await q.cancelQueries([QueryKey.TextResourcesForOrg, org]);
+      // Save previous data
+      const previousData = q.getQueryData([QueryKey.TextResourcesForOrg, org]);
+      // Optimistically update the cache
+      q.setQueryData([QueryKey.TextResourcesForOrg, org], (old: ITextResourcesWithLanguage[]) => {
+        // Update logic here
+        return old;
+      });
+      return { previousData };
+    },
   });
 };
frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts (1)

9-22: Consider adding error handling and optimistic updates.

The mutation hook is well-structured with good separation of concerns, but could benefit from additional features.

Consider adding:

  1. Error handling with onError callback
  2. Optimistic updates using onMutate
  3. Cache rollback on error using onError

Example implementation:

 export const useUpdateOrgCodeListMutation = (org: string) => {
   const queryClient = useQueryClient();
   const { updateCodeListForOrg } = useServicesContext();

   const mutationFn = ({ title, data }: UpdateOrgCodeListMutationArgs) =>
     updateCodeListForOrg(org, title, data);

   return useMutation({
     mutationFn,
     onSuccess: (newData: CodeListsResponse) => {
       queryClient.setQueryData([QueryKey.OrgCodeLists, org], newData);
     },
+    onError: (error, _, context) => {
+      // Rollback to previous state on error
+      if (context?.previousData) {
+        queryClient.setQueryData([QueryKey.OrgCodeLists, org], context.previousData);
+      }
+    },
+    onMutate: async ({ title, data }) => {
+      // Cancel outgoing fetches
+      await queryClient.cancelQueries([QueryKey.OrgCodeLists, org]);
+      // Save previous data
+      const previousData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]);
+      // Optimistically update the cache
+      queryClient.setQueryData([QueryKey.OrgCodeLists, org], (old: CodeListsResponse) => {
+        // Update logic here
+        return old;
+      });
+      return { previousData };
+    },
   });
 };
frontend/packages/shared/src/hooks/queries/useTextResourcesForOrgQuery.test.ts (1)

9-17: Consider adding error and edge case tests.

The test suite currently only covers the happy path. Consider adding tests for:

  • Error states
  • Empty or invalid language
  • Network failures
  • Loading states
frontend/packages/shared/src/hooks/mutations/useCreateTextResourcesForOrgMutation.test.ts (1)

12-22: Enhance test coverage with additional test cases.

Consider adding tests for:

  • Failed mutations
  • Invalid payloads
  • Network errors
  • Loading states
frontend/packages/shared/src/hooks/mutations/useUpdateTextResourcesForOrgMutation.test.ts (1)

12-28: Add beforeEach cleanup and error cases.

While the happy path is well tested, consider:

  1. Adding beforeEach(jest.clearAllMocks) for consistent test state
  2. Including tests for:
    • Failed mutations
    • Invalid text resources
    • Network errors
    • Loading states
frontend/packages/shared/src/constants.js (1)

3-3: Consider grouping related route constants.

The new route constants (APP_DASHBOARD_BASENAME and ORG_LIBRARY_BASENAME) are related to navigation. Consider grouping all route-related constants together for better organization.

Example grouping:

+ // Route basenames
  export const APP_DEVELOPMENT_BASENAME = '/editor';
  export const APP_DASHBOARD_BASENAME = '/app-dashboard';
  export const DASHBOARD_BASENAME = '/dashboard';
  export const ORG_LIBRARY_BASENAME = '/org-library';
  export const PREVIEW_BASENAME = '/preview';

Also applies to: 7-7

frontend/packages/shared/src/mocks/codeListsResponse.ts (1)

15-31: Consider adding more edge cases to codeList1.

While the mock data is well-structured, consider adding test cases for:

  • Empty description/helpText
  • Special characters in values
  • Very long text values
backend/src/Designer/Services/Interfaces/IAltinnGitRepositoryFactory.cs (1)

13-13: Fix typo in parameter documentation.

There's a typo in the word "identified" in the parameter documentation.

-        /// <param name="org">The organization owning the repository identfied by it's short name as defined in Gitea.</param>
+        /// <param name="org">The organization owning the repository identified by its short name as defined in Gitea.</param>
frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts (1)

43-57: Consider adding error case testing.

While the happy path is well tested, consider adding test cases for:

  • API failure scenarios
  • Network errors
  • Invalid input validation
frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts (1)

45-59: Consider adding validation test cases.

While the basic creation flow is well tested, consider adding test cases for:

  • Duplicate title handling
  • Invalid code list data validation
  • Empty title/data scenarios
frontend/packages/shared/src/mocks/textResourcesMock.ts (1)

1-87: Add documentation and consider i18n best practices.

The mock data structure is well-organized, but could benefit from:

  1. Documentation explaining the purpose and usage of these mocks
  2. Using i18n keys instead of hardcoded Norwegian text to align with internationalization practices

Consider adding JSDoc comments and refactoring to use i18n keys:

+/**
+ * Mock text resources for testing purposes.
+ * Used in conjunction with text resource API mutations and components.
+ */
 import type { ITextResource, ITextResourcesWithLanguage } from 'app-shared/types/global';

 export const label1TextResource: ITextResource = {
   id: 'label1',
-  value: 'Ledetekst 1',
+  value: 'shared.text_resources.label1',
 };
frontend/packages/shared/src/types/QueryKey.ts (1)

54-54: Maintain alphabetical ordering in enum.

The TextResourcesForOrg member should be placed alphabetically between TextResources and Widgets to maintain consistency.

-  TextResourcesForOrg = 'TextResourcesForOrg',
   DataType = 'DataType',

   // Resourceadm
+  TextResourcesForOrg = 'TextResourcesForOrg',
   ResourceList = 'ResourceList',
frontend/dashboard/pages/OrgContentLibrary/OrgContentLibrary.tsx (1)

82-86: Enhance error handling specificity.

The error handling could be more specific by handling different types of errors separately.

 onError: (error: AxiosError<ApiError>) => {
   if (isErrorUnknown(error)) {
     toast.error(t('dashboard.org_library.code_list_upload_generic_error'));
+  } else if (error.response?.status === 413) {
+    toast.error(t('dashboard.org_library.code_list_upload_size_error'));
+  } else if (error.response?.status === 400) {
+    toast.error(t('dashboard.org_library.code_list_upload_format_error'));
   }
 },
frontend/libs/studio-pure-functions/src/StringUtils/StringUtils.ts (1)

89-89: Optimize regex and add documentation.

The implementation can be improved in two ways:

  1. Simplify regex as multiple leading slashes can be handled with a single replacement
  2. Add JSDoc documentation for consistency with other methods
+  /**
+   * Removes leading slash from a string.
+   * @param str The string to process
+   * @returns The string with leading slash removed
+   */
-  static removeLeadingSlash = (str: string): string => str.replace(/^\//g, '');
+  static removeLeadingSlash = (str: string): string => str.replace(/^\//, '');
backend/src/Designer/Services/Interfaces/Organisation/IOrgCodeListService.cs (1)

60-60: Consider return type consistency for DeleteCodeList.

The DeleteCodeList method returns List<OptionListData> which seems unnecessary for a deletion operation. Consider returning a boolean or void instead.

-    public Task<List<OptionListData>> DeleteCodeList(string org, string developer, string codeListId, CancellationToken cancellationToken = default);
+    public Task<bool> DeleteCodeList(string org, string developer, string codeListId, CancellationToken cancellationToken = default);
frontend/dashboard/pages/PageLayout/DashboardHeader/DashboardHeader.tsx (1)

75-78: Consider simplifying path extraction logic.

The extractSecondLastRouterParam function could be simplified using array destructuring.

-function extractSecondLastRouterParam(pathname: string): string {
-  const pathnameArray = pathname.split('/');
-  return pathnameArray[pathnameArray.length - 2];
-}
+function extractSecondLastRouterParam(pathname: string): string {
+  const [..., secondLast = ''] = pathname.split('/').slice(0, -1);
+  return secondLast;
+}
backend/src/Designer/Controllers/Preview/InstancesController.cs (2)

189-189: Consider implementing dynamic options list support.

The TODO comment indicates missing functionality for dynamic options list based on language and source.

Would you like me to help implement the dynamic options list functionality or create an issue to track this task?


195-199: Review error handling approach.

Currently, when an option list is not found, an empty list is returned to prevent frontend issues. Consider:

  1. Logging the error for monitoring
  2. Adding a specific response status code or header to indicate the fallback behavior
 catch (NotFoundException)
 {
+    // Log the error for monitoring
+    _logger.LogWarning("Options list {optionListId} not found, returning empty list", optionListId);
     // Return empty list since app-frontend don't handle a null result
-    return Ok(new List<string>());
+    return StatusCode(StatusCodes.Status204NoContent, new List<string>());
 }
frontend/dashboard/pages/OrgContentLibrary/OrgContentLibrary.test.tsx (1)

158-168: Consider adding assertion for upload function parameters.

While the test verifies the function call and error message hiding, it would be beneficial to also assert the structure of the FormData being passed to the upload function.

 expect(queriesMock.uploadCodeListForOrg).toHaveBeenCalledWith(org, expect.any(FormData));
+const formDataArg = queriesMock.uploadCodeListForOrg.mock.calls[0][1];
+expect(formDataArg.get('file')).toBeTruthy();
+expect(formDataArg.get('file').name).toBe(`${codeListNameMock}.json`);
backend/src/Designer/Controllers/Preview/V3/OldInstancesController.cs (1)

30-30: Consider adding XML documentation for the constant.

Adding XML documentation for the OptionsFolderPath constant would improve code maintainability.

+/// <summary>
+/// The folder path where options are stored in the app repository.
+/// </summary>
 private const string OptionsFolderPath = "App/options/";
frontend/dashboard/pages/CreateService/CreateService.test.tsx (1)

315-326: Consider consolidating similar test cases.

The test cases for different context types (all, org) follow the same pattern. Consider using a parameterized test to reduce code duplication.

-it('should set cancel link to /all when selected context is all', async () => {
-  const selectedContext = SelectedContextType.All;
-  const subroute = Subroute.AppDashboard;
-  (useParams as jest.Mock).mockReturnValue({ selectedContext, subroute });
-
-  renderWithMockServices();
-  const cancelLink: HTMLElement = screen.getByRole('link', {
-    name: textMock('general.cancel'),
-  });
-
-  expect(cancelLink.getAttribute('href')).toBe(`/${subroute}/${selectedContext}`);
-});
-
-it('should set cancel link to /org when selected context is org', async () => {
-  const selectedContext = 'ttd';
-  const subroute = Subroute.AppDashboard;
-  (useParams as jest.Mock).mockReturnValue({ selectedContext, subroute });
-
-  renderWithMockServices();
-  const cancelLink: HTMLElement = screen.getByRole('link', {
-    name: textMock('general.cancel'),
-  });
-
-  expect(cancelLink.getAttribute('href')).toBe(`/${subroute}/${selectedContext}`);
-});
+it.each([
+  [SelectedContextType.All, 'all'],
+  ['ttd', 'org']
+])('should set cancel link to /%s when selected context is %s', async (selectedContext, contextType) => {
+  const subroute = Subroute.AppDashboard;
+  (useParams as jest.Mock).mockReturnValue({ selectedContext, subroute });
+
+  renderWithMockServices();
+  const cancelLink: HTMLElement = screen.getByRole('link', {
+    name: textMock('general.cancel'),
+  });
+
+  expect(cancelLink.getAttribute('href')).toBe(`/${subroute}/${selectedContext}`);
+});

Also applies to: 328-339

frontend/packages/shared/src/api/queries.ts (2)

184-185: Consider adding return type annotations for better type safety.

While TypeScript can infer the return type, explicit return type annotations improve code readability and maintainability.

-export const getCodeListsForOrg = async (org: string): Promise<CodeListsResponse> => Promise.resolve(codeListsResponse);
+export const getCodeListsForOrg = async (org: string): Promise<CodeListsResponse> => {
+  return Promise.resolve(codeListsResponse);
+};

182-185: Consider adding error handling for the mock implementation.

Even with mock data, it's good practice to simulate potential error scenarios that might occur in the real API.

 // Organisation library
-export const getCodeListsForOrg = async (org: string): Promise<CodeListsResponse> => Promise.resolve(codeListsResponse);
+export const getCodeListsForOrg = async (org: string): Promise<CodeListsResponse> => {
+  if (!org) {
+    throw new Error('Organization ID is required');
+  }
+  return Promise.resolve(codeListsResponse);
+};
backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs (2)

845-845: Consider using the provided optionsFolderPath parameter.

The CreateOrOverwriteOptionsList method is still using the hardcoded OptionsFolderPath constant. Consider updating it to use the dynamic optionsFolderPath parameter for consistency with the other methods.

-string optionsFilePath = Path.Combine(OptionsFolderPath, $"{optionsListId}.json");
+string optionsFilePath = Path.Combine(optionsFolderPath, $"{optionsListId}.json");

858-858: Consider using the provided optionsFolderPath parameter.

The DeleteOptionsList and UpdateOptionsListId methods are still using the hardcoded OptionsFolderPath constant. Consider updating them to use the dynamic optionsFolderPath parameter for consistency with the other methods.

-string optionsFilePath = Path.Combine(OptionsFolderPath, $"{optionsListId}.json");
+string optionsFilePath = Path.Combine(optionsFolderPath, $"{optionsListId}.json");

-string currentFilePath = Path.Combine(OptionsFolderPath, oldOptionsListFileName);
-string newFilePath = Path.Combine(OptionsFolderPath, newOptionsListFileName);
+string currentFilePath = Path.Combine(optionsFolderPath, oldOptionsListFileName);
+string newFilePath = Path.Combine(optionsFolderPath, newOptionsListFileName);

Also applies to: 874-875

frontend/language/src/nb.json (1)

235-238: Consider adding periods consistently to alert messages.

While the translations are clear and well-written, consider adding periods at the end of all alert messages for consistency, as some other alert messages in the file use periods. For example:

-  "dashboard.org_library.alert_no_org_selected": "For å vise biblioteket, må du velge en organisasjon i menyen øverst til høyre",
+  "dashboard.org_library.alert_no_org_selected": "For å vise biblioteket, må du velge en organisasjon i menyen øverst til høyre.",
-  "dashboard.org_library.alert_no_org_selected_no_access": "Hvis du ikke har tilgang til noen organisasjoner, må du be om tilgang fra en tjenesteeier",
+  "dashboard.org_library.alert_no_org_selected_no_access": "Hvis du ikke har tilgang til noen organisasjoner, må du be om tilgang fra en tjenesteeier."
🛑 Comments failed to post (7)
backend/src/Designer/Services/Implementation/Organisation/OrgCodeListService.cs (1)

38-38: ⚠️ Potential issue

Fix invalid list initialization syntax
Using List<OptionListData> codeLists = []; is not valid in C#. Change it to use object initialization or a constructor call.

- List<OptionListData> codeLists = [];
+ List<OptionListData> codeLists = new List<OptionListData>();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        List<OptionListData> codeLists = new List<OptionListData>();
frontend/packages/shared/src/hooks/queries/useTextResourcesForOrgQuery.ts (1)

12-13: ⚠️ Potential issue

Include parameters in the query key for proper caching.

The query key should include both org and language parameters to ensure proper caching and data invalidation.

Apply this diff to fix the query key:

-    queryKey: [QueryKey.TextResourcesForOrg],
+    queryKey: [QueryKey.TextResourcesForOrg, org, language],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    queryKey: [QueryKey.TextResourcesForOrg, org, language],
    queryFn: () => getTextResourcesForOrg(org, language),
frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts (1)

31-55: 🛠️ Refactor suggestion

Add error case tests.

The test suite should include cases for:

  • Upload failure
  • Invalid file format
  • Network errors

Example test case:

it('Handles upload failure correctly', async () => {
  const queryClient = createQueryClientMock();
  const uploadError = new Error('Upload failed');
  const uploadCodeListForOrg = jest.fn(() => Promise.reject(uploadError));
  const { result } = renderHookWithProviders(() => useUploadOrgCodeListMutation(org), {
    queryClient,
    queries: { uploadCodeListForOrg },
  });

  await expect(result.current.mutateAsync(file)).rejects.toThrow(uploadError);
  expect(queryClient.getQueryData([QueryKey.OrgCodeLists, org])).toBeUndefined();
});
frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts (1)

46-49: ⚠️ Potential issue

Fix incorrect hook usage in cache update test.

The test is using useCreateOrgCodeListMutation instead of useDeleteOrgCodeListMutation. This is testing the wrong mutation.

Apply this diff to fix the test:

-    const createCodeListForOrg = jest.fn(() => Promise.resolve([otherCodeList]));
-    const { result } = renderHookWithProviders(() => useCreateOrgCodeListMutation(org), {
+    const deleteCodeListForOrg = jest.fn(() => Promise.resolve([otherCodeList]));
+    const { result } = renderHookWithProviders(() => useDeleteOrgCodeListMutation(org), {
       queryClient,
-      queries: { createCodeListForOrg },
+      queries: { deleteCodeListForOrg },
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    const deleteCodeListForOrg = jest.fn(() => Promise.resolve([otherCodeList]));
    const { result } = renderHookWithProviders(() => useDeleteOrgCodeListMutation(org), {
      queryClient,
      queries: { deleteCodeListForOrg },
    });
frontend/dashboard/pages/CreateService/CreateService.tsx (1)

91-91: 💡 Verification agent

🧩 Analysis chain

Add validation for route parameters.

The href construction assumes both subroute and selectedContext are defined. Consider adding validation or fallback values to prevent potential navigation issues.

-          href: `/${subroute}/${selectedContext}`,
+          href: `/${subroute || ''}/${selectedContext || ''}`.replace(/\/+/g, '/'),

Let's verify the usage of these parameters:


🏁 Script executed:

#!/bin/bash
# Search for other usages of subroute and selectedContext in href construction
rg -l 'subroute.*selectedContext.*href|selectedContext.*subroute.*href'

Length of output: 73


Below is an additional verification step to further inspect the file for occurrences of both the parameters and any fallback validations. Please run the following shell script so we can see if there’s already any handling for fallback values in the CreateService.tsx file:

Once we verify that there isn’t already any fallback or validation logic around these parameters, we can confidently note that the original suggestion to add validation (or provide fallback values) remains necessary.


🏁 Script executed:

[run_scripts]
#!/bin/bash
echo "Searching for occurrences of 'subroute' in CreateService.tsx:"
rg -n "subroute" frontend/dashboard/pages/CreateService/CreateService.tsx

echo "Searching for occurrences of 'selectedContext' in CreateService.tsx:"
rg -n "selectedContext" frontend/dashboard/pages/CreateService/CreateService.tsx

echo "Searching for href construction in CreateService.tsx (check for any fallback validations):"
rg -n "href:" frontend/dashboard/pages/CreateService/CreateService.tsx

echo "Searching for fallback pattern usage (|| operator) in CreateService.tsx:"
rg -n "||" frontend/dashboard/pages/CreateService/CreateService.tsx

Length of output: 4635


Add validation for route parameters to prevent navigation issues.

The current href construction on line 91 assumes that both subroute and selectedContext are always defined, which could lead to broken URL paths if either is undefined. Consider adding fallback values or validation logic to ensure the URL remains well-formed. For example, you could modify the code as follows:

-          href: `/${subroute}/${selectedContext}`,
+          href: `/${subroute || ''}/${selectedContext || ''}`.replace(/\/+/g, '/'),

This change will help to avoid issues with extra or missing slashes when one of the parameters is undefined.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

          href: `/${subroute || ''}/${selectedContext || ''}`.replace(/\/+/g, '/'),
frontend/dashboard/pages/OrgContentLibrary/OrgContentLibrary.tsx (1)

35-37: 🛠️ Refactor suggestion

Add error handling to handleUpdate function.

The handleUpdate function should include error handling to provide feedback when the update fails.

 const handleUpdate = ({ title, codeList }: CodeListWithMetadata): void => {
-  updateOptionList({ title, data: codeList });
+  updateOptionList({ title, data: codeList }, {
+    onError: (error: AxiosError<ApiError>) => {
+      if (isErrorUnknown(error)) {
+        toast.error(t('dashboard.org_library.code_list_update_generic_error'));
+      }
+    },
+    onSuccess: () => {
+      toast.success(t('dashboard.org_library.code_list_update_success'));
+    },
+  });
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  const handleUpdate = ({ title, codeList }: CodeListWithMetadata): void => {
-  updateOptionList({ title, data: codeList });
+  updateOptionList({ title, data: codeList }, {
+    onError: (error: AxiosError<ApiError>) => {
+      if (isErrorUnknown(error)) {
+        toast.error(t('dashboard.org_library.code_list_update_generic_error'));
+      }
+    },
+    onSuccess: () => {
+      toast.success(t('dashboard.org_library.code_list_update_success'));
+    },
+  });
  };
backend/src/Designer/Services/Interfaces/Organisation/IOrgCodeListService.cs (1)

32-41: 🛠️ Refactor suggestion

Fix incorrect documentation for UpdateCodeList.

The documentation states "Adds a new option to the code list" but the method actually updates the entire code list.

     /// <summary>
-    /// Adds a new option to the code list.
+    /// Updates an existing code list with new contents.
     /// If the file already exists, it will be overwritten.
     /// </summary>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    /// <summary>
    /// Updates an existing code list with new contents.
    /// If the file already exists, it will be overwritten.
    /// </summary>
    /// <param name="org">Organisation</param>
    /// <param name="developer">Username of developer</param>
    /// <param name="codeListId">Name of the new code list</param>
    /// <param name="codeList">The code list contents</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken"/> that observes if operation is cancelled.</param>
    public Task<List<OptionListData>> UpdateCodeList(string org, string developer, string codeListId, List<Option> codeList, CancellationToken cancellationToken = default);

@TomasEng TomasEng self-assigned this Feb 14, 2025
@wrt95 wrt95 added the skip-manual-testing PRs that do not need to be tested manually label Feb 14, 2025
@wrt95 wrt95 merged commit 8901b09 into org-library-mvp Feb 14, 2025
3 checks passed
@wrt95 wrt95 deleted the fix/update-org-codelist-controller branch February 14, 2025 13:41
@TomasEng TomasEng removed their assignment Feb 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/content-library Area: Related to library for shared resources backend skip-documentation Issues where updating documentation is not relevant skip-manual-testing PRs that do not need to be tested manually skip-releasenotes Issues that do not make sense to list in our release notes solution/studio/designer Issues related to the Altinn Studio Designer solution. team/studio-domain1
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

3 participants