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

chore: Update NGINX worker_processes to match CPU cores #14872

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

mlqn
Copy link
Contributor

@mlqn mlqn commented Mar 5, 2025

Description

We recently experienced an issue in production where NGINX was rejecting all requests because all available connections were in use. Here is a sample of logs that were produced at that time:

2025/03/04 10:40:34 [alert] 20#20: 1024 worker_connections are not enough
2025/03/04 10:40:34 [alert] 20#20: 1024 worker_connections are not enough
2025/03/04 10:40:34 [alert] 20#20: 1024 worker_connections are not enough
2025/03/04 10:40:34 [alert] 20#20: 1024 worker_connections are not enough

I have investigated a bit and didn’t notice any problems with connections remaining needlessly open, but I noticed that worker_processes was set to 1, which means that it only uses a 1 process on 1 core, instead of using all available cores we have in dev/staging/production. I changed it to auto which will allow NGINX to run 1 process for each available core. This will significantly increase the number of possible connections (number of available CPUs * worker_connections) and should also improve performances.

Related Issue(s)

  • PR itself

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)

Summary by CodeRabbit

  • New Features
    • Introduced organizational content management that lets users create, update, delete, and upload code lists and text resources directly through the UI.
    • Enhanced dashboard navigation with dedicated views for app and organization library access.
  • Improvements
    • Refined error messages and tooltips within editors and settings for a smoother interaction.
    • Upgraded dependencies and adjusted server configurations to boost performance and stability.
  • Bug Fixes
    • Addressed issues in form handling and error notifications to streamline content management workflows.

Copy link
Contributor

coderabbitai bot commented Mar 5, 2025

📝 Walkthrough

Walkthrough

The pull request introduces a variety of changes across multiple files, primarily focusing on enhancing the functionality related to organization code lists and text resources. Key updates include the addition of new hooks for managing code lists and text resources, modifications to existing components to handle blur events, and the introduction of new test files to validate these functionalities. Additionally, several dependencies have been updated, and new constants and enums have been introduced to support these changes. No modifications were made to exported or public entity declarations.

Changes

File(s) Change Summary
charts/altinn-loadbalancer/templates/configmap.yaml
development/load-balancer/templates/nginx.conf.conf
Updated the worker_processes directive from 1; to auto;, enabling NGINX to automatically determine the worker count.
backend/packagegroups/NuGet.props Updated versions for Npgsql.EntityFrameworkCore.PostgreSQL from 9.0.3 to 9.0.4 and NuGet.Versioning from 6.13.1 to 6.13.2.
backend/src/Designer/Controllers/Organisation/OrgCodeListController.cs Added a new controller for managing organization code lists with multiple endpoints for CRUD operations.
backend/src/Designer/Controllers/Organisation/OrgTextController.cs Added a new controller for managing text resources with endpoints for retrieving, creating, and updating resources.
backend/src/Designer/Factories/AltinnGitRepositoryFactory.cs Introduced a new method for creating an AltinnOrgGitRepository.
backend/src/Designer/Infrastructure/GitRepository/AltinnOrgGitRepository.cs Added a new class for managing text resources and code lists within a Git repository.
backend/src/Designer/Infrastructure/ServiceRegistration.cs Registered new services for organization code lists and texts.
frontend/app-development/features/appPublish/components/ConfirmUndeployDialog/ConfirmUndeployDialog.test.tsx Updated error handling in tests to utilize structured error objects from createApiErrorMock.
frontend/packages/shared/src/api/mutations.ts Added new functions for creating, updating, deleting, and uploading organization code lists and text resources.
frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts Introduced a new hook for creating organization-specific code lists.
frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.ts Introduced a new hook for fetching organization-specific code lists.
frontend/packages/shared/src/mocks/queriesMock.ts Added mock functions for organization code lists and text resources.
frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.tsx Enhanced error handling logic by introducing a delete button for error states.

Possibly related PRs

  • fix: Support unsetting the text resource in the text resource picker #14496: The changes in the main PR, which involve modifying text resource handling and introducing a new label for unset options, are directly related to the changes in the retrieved PR, which also focuses on enhancing text resource functionality by adding an option to unset the text resource. Both PRs address similar aspects of text resource management in the application.

  • chore: PDF dependency patching #14497: The changes in the main PR, which involve updating the version of the azure-security-keyvault-secrets dependency, are related to the retrieved PR that also updates the same dependency version in the pom.xml file. Both PRs focus on dependency version updates within the same project context.

Suggested labels

ops/ci-cd, area/app-deploy, solution/platform

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.test.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-storybook".

(The package "eslint-plugin-storybook" was not found when loaded as a Node module from the directory "/frontend/libs/studio-components".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-storybook@latest --save-dev

The plugin "eslint-plugin-storybook" was referenced from the config file in "frontend/libs/studio-components/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-storybook".

(The package "eslint-plugin-storybook" was not found when loaded as a Node module from the directory "/frontend/libs/studio-components".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-storybook@latest --save-dev

The plugin "eslint-plugin-storybook" was referenced from the config file in "frontend/libs/studio-components/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditorRow/StudioCodeListEditorRow.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-storybook".

(The package "eslint-plugin-storybook" was not found when loaded as a Node module from the directory "/frontend/libs/studio-components".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-storybook@latest --save-dev

The plugin "eslint-plugin-storybook" was referenced from the config file in "frontend/libs/studio-components/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

  • 7 others
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
  • @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 the skip-releasenotes Issues that do not make sense to list in our release notes label Mar 5, 2025
@mlqn mlqn changed the title Update worker_processes to match CPU cores Update NGINX worker_processes to match CPU cores Mar 5, 2025
@mlqn mlqn changed the title Update NGINX worker_processes to match CPU cores chore: Update NGINX worker_processes to match CPU cores Mar 5, 2025
@mlqn mlqn marked this pull request as ready for review March 5, 2025 11:57
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.

Actionable comments posted: 0

🧹 Nitpick comments (2)
development/load-balancer/templates/nginx.conf.conf (1)

1-1: Good optimization for multicore environments!

Changing from a fixed value of 1 to auto allows NGINX to automatically determine the optimal number of worker processes based on the available CPU cores. This aligns perfectly with the PR objective to address the connection exhaustion issue by utilizing more CPU cores.

This change will significantly improve the load balancer's ability to handle concurrent connections, as the total connection capacity is determined by worker_processes * worker_connections. With the current worker_connections value of 1024, each CPU core can now handle up to 1024 connections, multiplying your overall capacity.

charts/altinn-loadbalancer/templates/configmap.yaml (1)

7-7: Good optimization for Kubernetes deployments!

Changing from a fixed value of 1 to auto allows NGINX to automatically determine the optimal number of worker processes based on the available CPU cores in your Kubernetes pods. This change is consistent with the modification in the development environment configuration.

This change will significantly improve the load balancer's ability to handle concurrent connections in production, addressing the issue described in the PR objectives.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6fc8b3 and eea67ad.

📒 Files selected for processing (2)
  • charts/altinn-loadbalancer/templates/configmap.yaml (1 hunks)
  • development/load-balancer/templates/nginx.conf.conf (1 hunks)
🔇 Additional comments (2)
development/load-balancer/templates/nginx.conf.conf (1)

3-5: Consider increasing worker_connections if needed

The PR objectives mentioned NGINX rejecting requests with logs stating "1024 worker_connections are not enough." While increasing worker processes will help, you might still hit the connection limit if your traffic is very high.

If after implementing this change you continue to see connection exhaustion, consider also increasing the worker_connections value. The optimal value depends on your system resources and traffic patterns, but common values range from 2048 to 4096 for high-traffic environments.

charts/altinn-loadbalancer/templates/configmap.yaml (1)

9-9: Consider increasing worker_connections if needed

Similar to the development configuration, you might want to consider increasing the worker_connections value if you continue to see connection exhaustion after this change.

The optimal value depends on your production resources and traffic patterns, but common values range from 2048 to 4096 for high-traffic environments. Make sure to test any changes to this value under load to find the optimal configuration.

Copy link

codecov bot commented Mar 11, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 95.83%. Comparing base (d40ce4f) to head (33356b1).

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #14872   +/-   ##
=======================================
  Coverage   95.83%   95.83%           
=======================================
  Files        1959     1959           
  Lines       25525    25525           
  Branches     2887     2887           
=======================================
  Hits        24462    24462           
  Misses        801      801           
  Partials      262      262           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 5

🧹 Nitpick comments (62)
frontend/packages/ux-editor/src/hooks/useLayoutSetIcon.test.tsx (1)

31-34: Use more specific assertions for expected icons

Instead of just asserting that the icon is not the default icon, consider checking for the specific expected icon type for more robust tests.

frontend/packages/ux-editor/src/components/Settings/SettingsTabs.tsx (1)

33-40: Consider extracting repeated alert to a variable or component

The same alert component is duplicated in both tab contents. Consider extracting it to a variable or a separate component to adhere to the DRY principle.

+ const wipAlert = (
+   <StudioAlert severity='info' className={classes.wipMessage}>
+     {t('ux_editor.settings.wip_message')}
+   </StudioAlert>
+ );

  <StudioTabs.Content value={Tabs.Navigation}>
-   <StudioAlert severity='info' className={classes.wipMessage}>
-     {t('ux_editor.settings.wip_message')}
-   </StudioAlert>
+   {wipAlert}
  </StudioTabs.Content>
  <StudioTabs.Content value={Tabs.Database}>
-   <StudioAlert severity='info' className={classes.wipMessage}>
-     {t('ux_editor.settings.wip_message')}
-   </StudioAlert>
+   {wipAlert}
  </StudioTabs.Content>
frontend/packages/ux-editor/src/hooks/useLayoutSetIcon.tsx (4)

13-25: Hook implementation is clean but could be more concise.

The hook logic is easy to follow with sequential condition checks for different layout set types. However, consider using a switch statement or object mapping for better maintainability.

export const useLayoutSetIcon = (
  layoutSetModel: LayoutSetModel,
): { icon: ReactElement; iconColor: StudioIconCardIconColors } => {
-  if (layoutSetModel.type == 'subform') return { icon: <ClipboardIcon />, iconColor: 'blue' };
-
-  if (layoutSetModel.task?.id == 'CustomReceipt')
-    return { icon: <ReceiptIcon />, iconColor: 'green' };
-  if (layoutSetModel.task?.type == 'data') return { icon: <DataTaskIcon />, iconColor: 'blue' };
-  if (layoutSetModel.task?.type == 'signing') return { icon: <SignTaskIcon />, iconColor: 'red' };
-  if (layoutSetModel.task?.type == 'payment') return { icon: <CardIcon />, iconColor: 'yellow' };
-
-  return { icon: <QuestionmarkIcon />, iconColor: 'grey' };
+  // Check type first
+  if (layoutSetModel.type === 'subform') return { icon: <ClipboardIcon />, iconColor: 'blue' };
+  
+  // Then check task properties
+  const taskId = layoutSetModel.task?.id;
+  const taskType = layoutSetModel.task?.type;
+  
+  if (taskId === 'CustomReceipt') return { icon: <ReceiptIcon />, iconColor: 'green' };
+  
+  // Map task types to icons and colors
+  const taskTypeMap = {
+    data: { icon: <DataTaskIcon />, iconColor: 'blue' as StudioIconCardIconColors },
+    signing: { icon: <SignTaskIcon />, iconColor: 'red' as StudioIconCardIconColors },
+    payment: { icon: <CardIcon />, iconColor: 'yellow' as StudioIconCardIconColors },
+  };
+  
+  return taskType && taskTypeMap[taskType] 
+    ? taskTypeMap[taskType] 
+    : { icon: <QuestionmarkIcon />, iconColor: 'grey' };
};

16-16: Use strict equality comparison.

Using == instead of === for equality checks is generally discouraged in TypeScript/JavaScript.

- if (layoutSetModel.type == 'subform') return { icon: <ClipboardIcon />, iconColor: 'blue' };
+ if (layoutSetModel.type === 'subform') return { icon: <ClipboardIcon />, iconColor: 'blue' };

18-19: Use strict equality comparison for task ID check.

- if (layoutSetModel.task?.id == 'CustomReceipt')
+ if (layoutSetModel.task?.id === 'CustomReceipt')

20-22: Use strict equality comparison for task type checks.

- if (layoutSetModel.task?.type == 'data') return { icon: <DataTaskIcon />, iconColor: 'blue' };
- if (layoutSetModel.task?.type == 'signing') return { icon: <SignTaskIcon />, iconColor: 'red' };
- if (layoutSetModel.task?.type == 'payment') return { icon: <CardIcon />, iconColor: 'yellow' };
+ if (layoutSetModel.task?.type === 'data') return { icon: <DataTaskIcon />, iconColor: 'blue' };
+ if (layoutSetModel.task?.type === 'signing') return { icon: <SignTaskIcon />, iconColor: 'red' };
+ if (layoutSetModel.task?.type === 'payment') return { icon: <CardIcon />, iconColor: 'yellow' };
frontend/packages/ux-editor/src/components/TaskNavigation/TaskCard.tsx (2)

23-25: Event handler has empty implementation.

The onClick handler has a TODO comment but doesn't prevent default behavior. Consider adding event.preventDefault() to ensure the event doesn't propagate unexpectedly.

onClick={(event: MouseEvent<HTMLButtonElement>) => {
  /* TODO: Implement editing mode */
+ event.preventDefault();
}

40-56: Consider adding accessibility attributes.

The StudioIconCard component should include proper accessibility attributes, especially for interactive elements.

Consider adding an aria-label or similar accessibility attributes to the StudioIconCard to improve screen reader support.

frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.ts (1)

1-7: Consider using a simple function instead of a static-only class

The static analysis tool correctly identifies that classes with only static members should be avoided. This approach adds unnecessary complexity without providing any benefits of object-oriented design.

-export class FileUtils {
-  static convertToFormData = (file: File): FormData => {
-    const formData = new FormData();
-    formData.append('file', file);
-    return formData;
-  };
-}
+export const convertToFormData = (file: File): FormData => {
+  const formData = new FormData();
+  formData.append('file', file);
+  return formData;
+};

This refactoring maintains the exact same functionality while simplifying the code and following functional programming principles, which are generally preferred in modern JavaScript/TypeScript development.

🧰 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/libs/studio-pure-functions/src/FileUtils/FileUtils.test.ts (1)

1-17: The test looks good, but consider improving the assertion method.

The test correctly verifies that a File object is appended to FormData under the key "file". However, using toBe() checks for reference equality, which assumes the implementation doesn't create a copy of the file. For more robust testing, consider checking file properties instead.

- expect(retrievedFile).toBe(file);
+ expect(retrievedFile).toBeInstanceOf(File);
+ expect((retrievedFile as File).name).toBe(fileName);
+ expect((retrievedFile as File).type).toBe(fileType);
+ expect((retrievedFile as File).size).toBe(fileContent.length);
frontend/dashboard/pages/OrgContentLibrary/utils.ts (1)

1-10: Function looks good, but consider a small optimization.

The implementation correctly identifies organization context types. For a slight performance improvement, consider declaring the notOrgContexts array outside the function to avoid recreating it on each call.

+const notOrgContexts: string[] = [
+  SelectedContextType.Self,
+  SelectedContextType.All,
+  SelectedContextType.None,
+];
+
 export function isOrg(contextType: string): boolean {
-  const notOrgContexts: string[] = [
-    SelectedContextType.Self,
-    SelectedContextType.All,
-    SelectedContextType.None,
-  ];
   return !notOrgContexts.includes(contextType);
 }
frontend/packages/shared/src/contexts/ServicesContext.tsx (1)

11-11: Enhanced error handling.

The added check for non-Axios errors improves error logging by ensuring that all error types are properly handled. This helps with debugging issues that might not come from HTTP requests.

However, consider using a more descriptive message with the error, such as:

- console.error(error);
+ console.error('Non-Axios error occurred in ServicesContext:', error);

This would make the logs more informative when troubleshooting.

Also applies to: 40-43

frontend/packages/shared/src/hooks/queries/useTextResourcesForOrgQuery.test.ts (1)

1-23: Well-structured test implementation

The test correctly verifies that the hook calls the expected function with the right parameters.

Consider adding tests for error handling scenarios to increase test coverage.

+ it('handles errors correctly', async () => {
+   queriesMock.getTextResourcesForOrg.mockRejectedValueOnce(new Error('Test error'));
+   const { result } = renderHookWithProviders(() => useTextResourcesForOrgQuery(org, languageMock));
+   await waitFor(() => expect(result.current.isError).toBe(true));
+   expect(result.current.error).toBeDefined();
+ });
frontend/libs/studio-components/src/components/StudioCodelistEditor/utils.ts (1)

77-80: Good implementation of the boolean code limit check

The function correctly implements a limit for boolean code lists, which makes sense as boolean values typically have only two options.

Consider extracting the booleanCodeLimit constant outside the function to improve maintainability:

+const BOOLEAN_CODE_LIMIT = 2;
+
 export function isCodeLimitReached(codeList: CodeList, codeType: CodeListItemType): boolean {
-  const booleanCodeLimit = 2;
-  return codeType === CodeListItemType.Boolean && codeList.length >= booleanCodeLimit;
+  return codeType === CodeListItemType.Boolean && codeList.length >= BOOLEAN_CODE_LIMIT;
 }
frontend/testing/playwright/components/DashboardHeader.ts (1)

1-17: Well-structured Playwright component class, but with an unnecessary constructor.

The DashboardHeader class is well designed, but the constructor doesn't add any functionality beyond what's provided by the parent class.

Consider simplifying by removing the unnecessary constructor:

-  constructor(page: Page, environment?: Environment) {
-    super(page, environment);
-  }

The class will inherit the constructor from BasePage automatically.

🧰 Tools
🪛 Biome (1.9.4)

[error] 8-10: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

frontend/packages/shared/src/hooks/queries/useTextResourcesForOrgQuery.ts (1)

1-15: Good implementation of the custom hook for fetching text resources

This custom hook is well-structured and follows React Query best practices:

  • Proper type definitions for parameters and return values
  • Effective use of context to access the service function
  • Clean implementation of the query configuration

One minor observation: the query key only includes QueryKey.TextResourcesForOrg but doesn't include the organization ID or language parameters as part of the key. This could potentially cause issues with caching if you need to fetch resources for multiple organizations or languages.

Consider including the org and language in the query key for proper cache management:

  return useQuery<ITextResourcesWithLanguage>({
-   queryKey: [QueryKey.TextResourcesForOrg],
+   queryKey: [QueryKey.TextResourcesForOrg, org, language],
    queryFn: () => getTextResourcesForOrg(org, language),
  });
frontend/packages/shared/src/api/queries.ts (1)

190-190: Temporary mock with clear TODO comment

The function uses a mock response with a well-documented TODO comment that includes a GitHub issue reference, making it clear this is a temporary solution pending API implementation.

Consider separating the TODO comment to its own line for better readability:

-export const getTextResourcesForOrg = async (org: string, language: string): Promise<ITextResourcesWithLanguage> => Promise.resolve(textResourcesMock); // Todo: Replace with real API call when endpoint is ready. https://github.com/Altinn/altinn-studio/issues/14503
+// Todo: Replace with real API call when endpoint is ready. https://github.com/Altinn/altinn-studio/issues/14503
+export const getTextResourcesForOrg = async (org: string, language: string): Promise<ITextResourcesWithLanguage> => Promise.resolve(textResourcesMock);
frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts (1)

43-57: Comprehensive cache update verification

Good test that validates both the API call and the subsequent cache update. Consider adding assertion messages to make test failures more descriptive.

You could make test failures more descriptive by adding messages to your expect statements:

-    expect(updatedData).toEqual(expectedUpdatedData);
+    expect(updatedData).toEqual(expectedUpdatedData, 'Cache should be updated with API response');
backend/tests/Designer.Tests/Controllers/ApiTests/DesignerEndpointsTestsBase.cs (1)

136-139: Consider checking directory existence before deletion

The deletion logic could throw an exception if the directory doesn't exist. Consider checking first with Directory.Exists().

 if (!string.IsNullOrWhiteSpace(TestOrgPath))
 {
+    if (Directory.Exists(TestOrgPath))
+    {
         Directory.Delete(TestOrgPath, true);
+    }
 }
backend/src/Designer/Services/Interfaces/Organisation/IOrgTextsService.cs (1)

31-40: Minor typo in UpdateTextsForKeys documentation

There's a small typo in the documentation comment - "resouce" should be "resource".

-    /// Updates values for specified keys in the text resouce.
+    /// Updates values for specified keys in the text resource.

Additionally, based on the retrieved learning, this method assumes the text resource already exists. Consider adding this precondition to the documentation.

backend/tests/Designer.Tests/Controllers/OrgTextController/CreateResourceTests.cs (1)

1-44: Consider adding more test cases with invalid inputs

While the current test covers valid inputs, consider adding tests for invalid inputs such as invalid language codes, malformed JSON payloads, or missing fields to ensure proper error handling.

frontend/dashboard/pages/PageLayout/DashboardHeader/DashboardHeader.tsx (1)

79-82: Consider edge cases in route parameter extraction

The extractSecondLastRouterParam function is simple but could fail with edge cases like URLs with only one segment. Consider adding safeguards.

-function extractSecondLastRouterParam(pathname: string): string {
-  const pathnameArray = pathname.split('/');
-  return pathnameArray[pathnameArray.length - 2];
-}
+function extractSecondLastRouterParam(pathname: string): string {
+  const pathnameArray = pathname.split('/').filter(Boolean);
+  return pathnameArray.length > 1 ? pathnameArray[pathnameArray.length - 2] : '';
+}
backend/tests/Designer.Tests/Infrastructure/GitRepository/AltinnOrgGitRepositoryTests.cs (1)

119-136: Fix method name typo

There's a typo in the method name: "CreatCodeList" should be "CreateCodeList" to match the naming pattern used in other tests.

-public async Task CreatCodeList_WithEmptyRepo_ShouldCreateCodeList(string repository, string codeListId)
+public async Task CreateCodeList_WithEmptyRepo_ShouldCreateCodeList(string repository, string codeListId)
backend/tests/Designer.Tests/Services/OrgTextsServiceTests.cs (1)

117-117: Fix typo in test method name.

The method name contains a typo: "DoesNotExit" should be "DoesNotExist".

-    public async Task UpdateTextsForKeys_ShouldCreateNewIdWhenTextResourceDoesNotExit(string repo, string lang)
+    public async Task UpdateTextsForKeys_ShouldCreateNewIdWhenTextResourceDoesNotExist(string repo, string lang)
backend/src/Designer/Services/Implementation/Organisation/OrgTextsService.cs (1)

65-65: Clarify comment about handling non-existent keys.

The comment "handle if file not already exist" is inaccurate as the code below is actually handling the case where a specific key doesn't exist in the existing text resource.

-        // handle if file not already exist
+        // Handle keys that don't exist in the current text resource
backend/tests/Designer.Tests/Controllers/OrgCodeListController/CreateCodeListTests.cs (2)

52-57: Consider adding a null check for the deserialized response.
After deserializing the response into responseList, you immediately use responseList.Count on lines 56–57. If responseList is null (due to a deserialization issue or unexpected response), this will throw an exception. Adding a quick null assertion before accessing Count can make the tests more robust.

+ Assert.NotNull(responseList);
Assert.Equal(expectedResponse.Count, responseList.Count);

60-66: Use higher-level collection equality checks.
Within the for loop, you compare individual elements of expectedData and actualData. Consider using a higher-level collection comparison approach (like Assert.Equal on two lists or Assert.Collection) for better readability and to get more detailed error messages on failure.

backend/tests/Designer.Tests/Controllers/OrgCodeListController/UploadCodeListTests.cs (2)

35-50: Validate the response object to avoid null references.
Deserializing to List<OptionListData> could result in a null if JSON parsing fails or the API response differs. Consider checking for null before further assertions to prevent test crashes and improve debugging.


101-111: Consider verifying content beyond the status code.
When testing null values, you only assert BadRequest. If you’d like stronger guarantees, you could verify the error message or structure in the response to confirm the code list was indeed invalid.

frontend/dashboard/hooks/guards/useContextRedirectionGuard.ts (4)

23-24: Avoid immediate session storage writes within a conditional.
The current check and write to session storage occur outside a useEffect. This can lead to repeated writes during render cycles if the component re-renders and selectedContextType is not None. To ensure consistency, consider moving this logic inside a stable effect.

- if (selectedContextType !== SelectedContextType.None) {
-   typedSessionStorage.setItem('dashboard::selectedContext', selectedContextType);
- }
+ useEffect(() => {
+   if (selectedContextType !== SelectedContextType.None) {
+     typedSessionStorage.setItem('dashboard::selectedContext', selectedContextType);
+   }
+ }, [selectedContextType]);

75-81: Check for potential naming confusion.
navigateToSelf creates a DashboardRoute forcibly using SelectedContextType.Self while also reusing subroute. If selectedContextType is something else, this might cause confusion about the final context. Consider clarifying or adding logs for better debugging.


84-87: Consider handling cases where location is undefined.
The call location.search presumes a global object is available. In certain test environments (or older React versions), location might not be defined. If so, you could fallback to an empty string.


101-103: Refine the check for context differences.
isTargetContextDifferent compares getTargetContext(selectedContextType) to selectedContextType. You may reduce overhead by storing the result of getTargetContext to avoid calling it repeatedly in other parts of the code.

backend/tests/Designer.Tests/Controllers/OrgTextController/UpdateResourceTests.cs (3)

24-48: Consider adding negative test scenarios.
The UpdateResource_Returns200OK_WithValidInput test verifies the expected behavior with valid input. Expanding coverage to include invalid or missing fields in updateDictionary would strengthen confidence in error handling.


50-77: Preserve broader variable integrity in the second test.
Currently, the test checks only the number of variables in TextUsingVariables. Consider asserting that other text elements with variables remain unchanged, ensuring no accidental modifications to unrelated text IDs.


91-105: Ensure consistent insertion order for new text elements.
When inserting new keys at index 0 inside PrepareExpectedResourceWithoutVariables, existing entries are pushed back. If maintaining original order is desired, consider appending them instead. Otherwise, document the intended new-element-first logic.

frontend/packages/shared/src/mocks/queriesMock.ts (8)

87-87: Return realistic mock data for test reliability.
Currently returning an empty array may limit test coverage. Consider returning at least one mock code list to simulate real workflows.


201-205: Provide variant scenarios for text resources.
Returning the same mock resource each time ensures consistency, but adding slight variations in language or structure could improve test depth.


225-225: Consider returning a newly created code list.
createCodeListForOrg might return a mock code list object or ID to help consumers verify that creation succeeded.


233-233: Return details for created text resources.
createTextResourcesForOrg could return the newly created resource keys to confirm success in tests.


235-235: Validate input in mock deletion.
deleteCodeListForOrg always resolves without verifying an ID. Optional checks or logging could surface potential mismatches in test usage.


263-263: Return updated code list object for better testing.
updateCodeListForOrg might provide a modified code list or status message, letting tests confirm the update was applied.


265-265: Enhance updating text resources with a verifiable result.
Similar to code lists, returning updated text resource data strengthens test validation.


266-266: Provide success feedback for code list upload.
uploadCodeListForOrg might return metadata (e.g., file name, status) to simulate real upload results in tests.

frontend/dashboard/app/App.tsx (3)

25-39: Clear switch-based status handling.
Using mergeQueryStatuses and a switch statement provides concise clarity. A fallback default case could handle unexpected states or logging as an extra safeguard.


50-72: Differentiated error messages are good but strictly bound to user error.
The logic distinguishes between user data errors and organization data errors. Consider a scenario where both fail simultaneously, ensuring it's still user-friendly.


92-103: Subroute guard covers known segments.
A default fallback could help surface or handle unknown routes gracefully. Otherwise, good handling for recognized subroutes.

backend/src/Designer/Services/Interfaces/Organisation/IOrgCodeListService.cs (2)

43-51: Clarify documentation for UploadCodeList method

The XML comments for UploadCodeList state that it "Creates a new code list in the org repository" which is identical to the comments for CreateCodeList. It would be more accurate to describe this method's specific functionality of uploading from a file.

/// <summary>
-/// Creates a new code list in the org repository.
+/// Uploads and creates a new code list from a file in the org repository.
/// If the file already exists, it will be overwritten.
/// </summary>

53-60: Fix typo in documentation for DeleteCodeList method

There's a small grammatical error in the XML comment.

/// <summary>
-/// Deletes an code list from the org repository.
+/// Deletes a code list from the org repository.
/// </summary>
frontend/packages/shared/src/api/mutations.ts (1)

190-192: Address TODO comment with a concrete plan

The TODO comments indicate these functions need to be updated once the endpoints are ready. Consider adding a GitHub issue number or a more specific timeline to track this work.

While there is a GitHub issue reference, it would be helpful to include an estimated timeline or milestone for when these placeholder implementations will be replaced with real API calls.

backend/tests/Designer.Tests/Services/OrgCodeListServiceTests.cs (2)

148-151: Fix trailing comma in JSON test data

The JSON string has a trailing comma after the last element which may cause issues with some JSON parsers.

const string jsonCodeList = @"[
    {""label"": ""someLabel"",""value"": ""someValue"" },
-]";
+]";

161-171: Use using statement for proper resource disposal

The memory stream is manually closed after the service call. It would be better to use a using statement to ensure proper disposal even if an exception occurs.

-var stream = new MemoryStream(codeListBytes);
-IFormFile file = new FormFile(stream, 0, codeListBytes.Length, fileName, fileName);
-
-TargetOrg = TestDataHelper.GenerateTestOrgName();
-string targetRepository = TestDataHelper.GetOrgContentRepoName(TargetOrg);
-await TestDataHelper.CopyOrgForTest(Developer, Org, Repo, TargetOrg, targetRepository);
-var service = GetOrgCodeListService();
-
-// Act
-var codeListData = await service.UploadCodeList(TargetOrg, Developer, file);
-stream.Close();
+using (var stream = new MemoryStream(codeListBytes))
+{
+    IFormFile file = new FormFile(stream, 0, codeListBytes.Length, fileName, fileName);
+
+    TargetOrg = TestDataHelper.GenerateTestOrgName();
+    string targetRepository = TestDataHelper.GetOrgContentRepoName(TargetOrg);
+    await TestDataHelper.CopyOrgForTest(Developer, Org, Repo, TargetOrg, targetRepository);
+    var service = GetOrgCodeListService();
+
+    // Act
+    var codeListData = await service.UploadCodeList(TargetOrg, Developer, file);
+}
backend/tests/Designer.Tests/Controllers/OrgCodeListController/UpdateCodeListTests.cs (1)

42-43: Consider clarifying duplicate labels in test data

There are duplicate entries with the same label "NumberValue" in the test data. While this might be intentional to test how the system handles duplicates, it could be confusing. Consider adding a comment explaining this choice or using distinct labels.

Also applies to: 93-94

backend/src/Designer/Controllers/Organisation/OrgCodeListController.cs (1)

132-155: Reassess returning all code lists after delete.

Currently, the DeleteCodeList method returns the entire set of code lists after removal. This could be inefficient and might not scale when organizations have many code lists. Consider returning only a confirmation or the metadata of the deleted item for better performance and clarity.

backend/src/Designer/Services/Implementation/Organisation/OrgCodeListService.cs (3)

38-38: Use explicit list instantiation for clarity.

The syntax List<OptionListData> codeLists = []; may not be supported in earlier C# versions and could cause confusion. An explicit instantiation such as new List<OptionListData>() is more universally recognized and clear.

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

103-107: Ensure robust handling of invalid attributes.

Currently, an exception is thrown for the first invalid item encountered (Value or Label is null). If partial acceptance of valid items is desired, or if more detailed error reporting is needed, consider collecting all invalid items to provide richer feedback or partial success responses.


68-78: Avoid returning all code lists for create/update operations.

Returning the entire list of code lists after creating or updating one might be inefficient in organizations with many code lists. Consider returning only the newly created/updated code list and letting clients fetch the entire list separately if needed.

Also applies to: 80-91

frontend/testing/playwright/tests/org-library/codelists.spec.ts (1)

20-20: Serial mode usage notice.

Enabling serial mode (test.describe.configure({ mode: 'serial' })) ensures these tests run in sequence, preventing cross-test interference. This is beneficial if the environment is not stateless. Retraining parallel test execution or implementing thorough cleanup steps could make the tests more scalable.

frontend/testing/playwright/pages/OrgLibraryPage/CodeLists.ts (4)

28-35: Consider making timeout configurable

The hardcoded timeout of 8000ms might be problematic in environments with varying performance characteristics.

- await expect(modalTitle).toBeVisible({ timeout: 8000 });
+ const modalTimeout = process.env.MODAL_TIMEOUT || 8000;
+ await expect(modalTitle).toBeVisible({ timeout: parseInt(modalTimeout) });

45-51: Potential code duplication

The methods clickOnAddAlternativeButton and clickOnAddItemButton appear to perform identical actions. Consider consolidating them if they're intended to do the same thing.

- public async clickOnAddAlternativeButton(): Promise<void> {
-   await this.page
-     .getByRole('button', {
-       name: this.textMock('code_list_editor.add_option'),
-     })
-     .click();
- }

// Later in the file...
// Keep this method and use it in place of clickOnAddAlternativeButton
public async clickOnAddItemButton(): Promise<void> {
  await this.page
    .getByRole('button', {
      name: this.textMock('code_list_editor.add_option'),
    })
    .click();
}

Also applies to: 107-113


161-168: Consider more robust file path handling

The current approach to file path management might be brittle if file locations change. Consider using a more centralized approach for test assets.

- await fileChooser.setFiles(path.join(__dirname, fileName));
+ // Assuming you have a constant or config for test assets directory
+ const TEST_ASSETS_DIR = process.env.TEST_ASSETS_DIR || path.join(__dirname, '../../../assets');
+ await fileChooser.setFiles(path.join(TEST_ASSETS_DIR, fileName));

190-203: Improve selector robustness

The XPath selector xpath=.. used to navigate to the parent element is fragile. Consider using more robust selectors.

- const accordion = accordionTitle.locator('xpath=..');
+ // Using data attributes or more specific selectors would be more robust
+ const accordionId = await accordionTitle.getAttribute('id');
+ const accordion = this.page.locator(`[aria-labelledby="${accordionId}"]`);
🛑 Comments failed to post (5)
frontend/packages/ux-editor/src/components/TaskNavigation/TaskCardBar.tsx (2)

16-22: 🛠️ Refactor suggestion

Ensure layoutSetsModel.sets exists before mapping

There's a potential runtime error if layoutSetsModel.sets is undefined. Add a null check or provide a fallback empty array.

  return (
    <div className={classes.container}>
-      {layoutSetsModel.sets.map((layoutSetModel) => (
+      {layoutSetsModel?.sets?.map((layoutSetModel) => (
        <TaskCard key={layoutSetModel.id} layoutSetModel={layoutSetModel} />
      ))}
    </div>
  );
📝 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.

  return (
    <div className={classes.container}>
      {layoutSetsModel?.sets?.map((layoutSetModel) => (
        <TaskCard key={layoutSetModel.id} layoutSetModel={layoutSetModel} />
      ))}
    </div>
  );

14-14: 🛠️ Refactor suggestion

Add error handling for failed queries

The component only handles the loading state but lacks error handling. If the query fails, the component will render with potentially undefined data.

-  if (layoutSetsPending) return null;
+  if (layoutSetsPending || !layoutSetsModel) return null;
📝 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.

  if (layoutSetsPending || !layoutSetsModel) return null;
frontend/packages/ux-editor/src/components/TaskNavigation/TaskCard.tsx (2)

52-54: ⚠️ Potential issue

Button needs an onClick handler.

The primary button doesn't have an onClick handler defined, which makes it non-functional.

-<StudioButton color='second' variant='primary'>
+<StudioButton 
+  color='second' 
+  variant='primary'
+  onClick={() => {
+    /* TODO: Implement navigation to UX editor */
+  }}
+>
  {t('ux_editor.task_card.ux_editor')}
</StudioButton>
📝 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.

      <StudioButton 
        color='second' 
        variant='primary'
        onClick={() => {
          /* TODO: Implement navigation to UX editor */
        }}
      >
        {t('ux_editor.task_card.ux_editor')}
      </StudioButton>

14-38: 💡 Verification agent

🧩 Analysis chain

Implement TODOs for edit and delete functionality.

The component has TODOs for the edit and delete functionality. These need to be implemented before the component is fully functional.


🏁 Script executed:

#!/bin/bash
# Check if there are other TODO comments in the codebase related to TaskCard functionality
rg "TODO.*TaskCard" --type ts --type tsx
rg "TODO.*task.*edit" --type ts --type tsx
rg "TODO.*task.*delete" --type ts --type tsx

Length of output: 218


Below is a revised approach to help verify and address the concerns raised. It appears that the initial search failed due to an unrecognized file type ("tsx"). To gather the necessary context, please run the following updated shell script that registers the TSX file type and searches for the exact TODO comments in the TaskCard component:

Once you verify that these TODOs still exist in the file (frontend/packages/ux-editor/src/components/TaskNavigation/TaskCard.tsx), please address them by implementing the actual editing and deletion logic rather than leaving placeholder comments.


Action Item: Implement Functional Edit and Delete Handlers in TaskCard

  • Replace the editing mode placeholder (/* TODO: Implement editing mode */) with code that toggles the edit view or opens an editor modal.
  • Replace the delete callback placeholder (/* TODO: Call delete on task */) with logic to call the proper delete function or API, ensuring that it updates the state accordingly.

Please ensure that these functionalities are fully implemented before merging the component changes.


🏁 Script executed:

#!/bin/bash
# Register TSX file type for ripgrep and search for TaskCard-related TODOs in TSX files

# Search for the TODO comment for implementing the editing mode
rg --type-add 'tsx:*.tsx' "TODO: Implement editing mode" --type tsx || echo "No 'editing mode' TODO found in TaskCard"

# Search for the TODO comment for calling delete on task
rg --type-add 'tsx:*.tsx' "TODO: Call delete on task" --type tsx || echo "No 'delete' TODO found in TaskCard"

Length of output: 487


Action Required: Implement Functional Edit and Delete Handlers

The TaskCard component in frontend/packages/ux-editor/src/components/TaskNavigation/TaskCard.tsx still contains the following placeholder TODO comments:

  • Editing Functionality:
    Replace /* TODO: Implement editing mode */ with logic to toggle an edit mode or open an editor modal.

  • Deletion Functionality:
    Replace /* TODO: Call delete on task */ with handling that properly deletes the task (updating state and/or making necessary API calls).

Please implement these functionalities to ensure that the component is fully operational before merging this PR.

backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/org-content/Codelists/codeListTrailingComma.json (1)

1-3: ⚠️ Potential issue

Fix invalid JSON trailing comma

The JSON file contains a trailing comma after the last item in the array, which is not valid according to the JSON specification. Although some parsers might accept this, it's better to follow the standard for broader compatibility.

[
-    { "label": "someLabel", "value":  "someValue"},
+    { "label": "someLabel", "value":  "someValue"}
]
📝 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.

[
    { "label": "someLabel", "value":  "someValue"}
]
🧰 Tools
🪛 Biome (1.9.4)

[error] 2-3: Expected an array, an object, or a literal but instead found ']'.

Expected an array, an object, or a literal here.

(parse)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
added-to-sprint kind/chore skip-releasenotes Issues that do not make sense to list in our release notes team/studio-core
Projects
Status: 🔎 Review
Development

Successfully merging this pull request may close these issues.

2 participants