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

feat(ui-editor): add lookup components #14516

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

Conversation

nkylstad
Copy link
Member

@nkylstad nkylstad commented Jan 27, 2025

Description

Added new lookup-components as described by layout jsonschema:

  • OrganisationLookup
  • PersonLookup

Added new endpoints in PreviewController to return mock response to the lookup-call from the components.
These endpoints return a basic string, with no typing.
Ideally we should use the Altinn.App.Api models as return types here, but upgrading from v8.0.0-rc1 to v8.5.0 seemed out of scope for this change 🤔
Suggest we create a separate issue to perform the upgrade, and consider introducing Altinn.App.Api as a dependency once that is done.

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)

Documentation

  • User documentation is updated with a separate linked PR in altinn-studio-docs. (if applicable)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Organisation Lookup component for retrieving organization details
    • Added Person Lookup component for retrieving personal information
    • Implemented backend endpoints to support lookup functionality
  • Technical Improvements

    • Enhanced component type definitions
    • Added JSON schemas for new lookup components
    • Created unit tests for new lookup functionality

The release introduces two new lookup components that allow users to retrieve organization and personal information within the application.

Copy link
Contributor

coderabbitai bot commented Jan 27, 2025

📝 Walkthrough

Walkthrough

This pull request introduces new functionality for organization and person lookup in the backend and frontend of the application. The changes include adding two new action methods in the PreviewController to simulate lookup services, creating corresponding test classes to validate these methods, and extending the frontend type definitions and component configurations to support organization and person lookup components. The implementation provides mock JSON responses and establishes the necessary type and schema definitions to integrate these new lookup features.

Changes

File Change Summary
backend/src/Designer/Controllers/PreviewController.cs Added two new action methods: OrganisationLookup (GET endpoint) and PersonLookup (POST endpoint) to provide mock lookup responses
backend/tests/Designer.Tests/Controllers/... Added OrganisationLookupTests and PersonLookupTests to validate the new controller methods
frontend/packages/shared/src/types/ComponentSpecificConfig.ts Introduced new types DataModelBindingsOrganisationLookup and DataModelBindingsPersonLookup
frontend/packages/shared/src/types/ComponentType.ts Added OrganisationLookup and PersonLookup to the ComponentType enum
frontend/packages/ux-editor/src/data/formItemConfig.ts Added configurations for OrganisationLookup and PersonLookup components
frontend/packages/ux-editor/src/testing/componentSchemaMocks.ts Imported and added schemas for new component types
frontend/packages/ux-editor/src/testing/schemas/json/component/ Added JSON schemas for OrganisationLookup.schema.v1.json and PersonLookup.schema.v1.json

Possibly related PRs

Suggested labels

added-to-sprint, team/studio-domain2, kind/bug

Suggested reviewers

  • Jondyr
  • mlqn
  • JamalAlabdullah
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

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/ui-editor Area: Related to the designer tool for assembling app UI in Altinn Studio. 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 Jan 27, 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.

Actionable comments posted: 5

🧹 Nitpick comments (10)
backend/tests/Designer.Tests/Controllers/PreviewController/OrganisationLookupTests.cs (1)

12-12: Use a more descriptive constant name.

Consider renaming the constant to better indicate its test purpose:

-    protected const string organisationNumber = "123456789";
+    protected const string VALID_TEST_ORGANISATION_NUMBER = "123456789";
frontend/packages/shared/src/types/ComponentSpecificConfig.ts (2)

52-55: Add JSDoc comments to document the type.

Consider adding JSDoc comments to document the purpose of the type and its properties:

+/**
+ * Data model bindings for the OrganisationLookup component
+ */
 type DataModelBindingsOrganisationLookup = {
+  /** Organization number binding (required) */
   organisation_lookup_orgnr: string | InternalBindingFormat;
+  /** Organization name binding (optional) */
   organisation_lookup_name?: string | InternalBindingFormat;
 };

57-60: Add JSDoc comments to document the type.

Consider adding JSDoc comments to document the purpose of the type and its properties:

+/**
+ * Data model bindings for the PersonLookup component
+ */
 type DataModelBindingsPersonLookup = {
+  /** Social security number binding (required) */
   person_lookup_ssn: string | InternalBindingFormat;
+  /** Person name binding (required) */
   person_lookup_name: string | InternalBindingFormat;
 };
frontend/packages/ux-editor/src/data/formItemConfig.ts (2)

393-398: Add default data model bindings and consider using a more appropriate icon.

  1. Add default data model bindings to match the pattern of other form components:
 [ComponentType.OrganisationLookup]: {
   name: ComponentType.OrganisationLookup,
   itemType: LayoutItemType.Component,
-  defaultProperties: {},
+  defaultProperties: {
+    dataModelBindings: {
+      organisation_lookup_orgnr: '',
+      organisation_lookup_name: ''
+    }
+  },
   icon: ShortTextIcon,
 },
  1. Consider using a more appropriate icon (e.g., GroupIcon or ClipboardIcon) that better represents an organization lookup.

431-436: Add default data model bindings and consider using a more appropriate icon.

  1. Add default data model bindings to match the pattern of other form components:
 [ComponentType.PersonLookup]: {
   name: ComponentType.PersonLookup,
   itemType: LayoutItemType.Component,
-  defaultProperties: {},
+  defaultProperties: {
+    dataModelBindings: {
+      person_lookup_ssn: '',
+      person_lookup_name: ''
+    }
+  },
   icon: ShortTextIcon,
 },
  1. Consider using a more appropriate icon (e.g., PersonIcon or similar) that better represents a person lookup.
backend/src/Designer/Controllers/PreviewController.cs (1)

679-692: Enhance the organization lookup endpoint with validation and error handling.

Consider the following improvements:

  1. Add input validation for the organization number format
  2. Return more realistic mock data with additional fields
  3. Add error handling for invalid organization numbers
 [HttpGet]
 [Route("api/v1/lookup/organisation/{organisationNumber}")]
 public IActionResult OrganisationLookup(string org, string app, string organisationNumber)
 {
+    // Validate organization number format (e.g., 9 digits for Norwegian org numbers)
+    if (!Regex.IsMatch(organisationNumber, @"^\d{9}$"))
+    {
+        return BadRequest(new { success = false, message = "Invalid organization number format" });
+    }
+
+    // Return more realistic mock data
     string lookupResponse = $@"{{
         ""success"": true,
         ""organisationDetails"": {{
             ""orgNr"": ""{organisationNumber}"",
-            ""name"": ""Test AS (preview)""
+            ""name"": ""Test AS (preview)"",
+            ""organizationType"": ""AS"",
+            ""status"": ""Active"",
+            ""address"": {{
+                ""street"": ""Test Street 1"",
+                ""postalCode"": ""0123"",
+                ""city"": ""Oslo""
+            }}
         }}
     }}";
     return Ok(lookupResponse);
 }
frontend/packages/ux-editor/src/testing/schemas/json/component/OrganisationLookup.schema.v1.json (1)

1-148: Add organization-specific validation patterns and configuration options.

The schema is well-structured but could be enhanced with:

  1. Add validation pattern for organization number binding:
 "dataModelBindings": {
   "type": "object",
   "properties": {
-    "organisation_lookup_orgnr": { "$ref": "#/definitions/IRawDataModelBinding" },
+    "organisation_lookup_orgnr": {
+      "$ref": "#/definitions/IRawDataModelBinding",
+      "pattern": "^\\d{9}$",
+      "description": "Must be a 9-digit organization number"
+    },
     "organisation_lookup_name": { "$ref": "#/definitions/IRawDataModelBinding" }
   },
   "required": ["organisation_lookup_orgnr"],
   "additionalProperties": false
 }
  1. Add configuration options for the lookup behavior:
 {
   "properties": {
+    "lookupOptions": {
+      "type": "object",
+      "properties": {
+        "debounceMs": {
+          "type": "number",
+          "description": "Debounce time in milliseconds for the lookup request",
+          "default": 300
+        },
+        "minLength": {
+          "type": "number",
+          "description": "Minimum length of input before triggering lookup",
+          "default": 9
+        },
+        "validateOnBlur": {
+          "type": "boolean",
+          "description": "Whether to validate the organization number on blur",
+          "default": true
+        }
+      },
+      "additionalProperties": false
+    },
     // ... existing properties
   }
 }
frontend/packages/ux-editor/src/testing/schemas/json/component/PersonLookup.schema.v1.json (3)

5-10: Consider enhancing ID pattern documentation.

While the pattern is well-defined, it would be helpful to include examples of valid and invalid IDs in the description to make it more developer-friendly.

Consider updating the description to include examples:

-      "description": "The component ID. Must be unique within all layouts/pages in a layout-set. Cannot end with <dash><number>.",
+      "description": "The component ID. Must be unique within all layouts/pages in a layout-set. Cannot end with <dash><number>. Valid examples: 'person-lookup', 'person2', 'lookup-123456'. Invalid examples: 'person-123', '-person', '123'.",

28-49: Consider using enum for page break values.

Instead of using examples and string references, consider defining an enum type for the break values to ensure type safety.

       "breakBefore": {
         "title": "Page break before",
         "description": "PDF only: Value or expression indicating whether a page break should be added before the component. Can be either: 'auto' (default), 'always', or 'avoid'.",
-        "examples": ["auto", "always", "avoid"],
+        "enum": ["auto", "always", "avoid"],
         "default": "auto",
-        "$ref": "expression.schema.v1.json#/definitions/string"
+        "type": "string"
       },

91-135: Consider additional accessibility text bindings.

While the schema includes summaryAccessibleTitle, consider adding more accessibility-focused text bindings for screen readers.

Consider adding:

  • ariaLabel: For the main component
  • errorMessageAriaLabel: For validation errors
  • helpTextAriaLabel: For help text that might differ from visual text
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7e0f720 and f3f95a7.

📒 Files selected for processing (9)
  • backend/src/Designer/Controllers/PreviewController.cs (1 hunks)
  • backend/tests/Designer.Tests/Controllers/PreviewController/OrganisationLookupTests.cs (1 hunks)
  • backend/tests/Designer.Tests/Controllers/PreviewController/PersonLookupTests.cs (1 hunks)
  • frontend/packages/shared/src/types/ComponentSpecificConfig.ts (2 hunks)
  • frontend/packages/shared/src/types/ComponentType.ts (1 hunks)
  • frontend/packages/ux-editor/src/data/formItemConfig.ts (4 hunks)
  • frontend/packages/ux-editor/src/testing/componentSchemaMocks.ts (2 hunks)
  • frontend/packages/ux-editor/src/testing/schemas/json/component/OrganisationLookup.schema.v1.json (1 hunks)
  • frontend/packages/ux-editor/src/testing/schemas/json/component/PersonLookup.schema.v1.json (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
frontend/packages/shared/src/types/ComponentSpecificConfig.ts

[error] 351-351: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 353-353: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🔇 Additional comments (7)
frontend/packages/shared/src/types/ComponentType.ts (1)

32-32: LGTM!

The new component types are properly added and follow the existing naming conventions and alphabetical ordering.

Also applies to: 37-37

frontend/packages/ux-editor/src/testing/componentSchemaMocks.ts (1)

31-31: Verify the existence and content of referenced schema files.

The new schema files are imported but not included in this PR. Please ensure:

  1. The schema files exist at the specified paths
  2. The schemas define appropriate validation rules for the lookup components

Also applies to: 36-36, 78-78, 83-83

✅ Verification successful

Schema files are present and properly structured

Both OrganisationLookup.schema.v1.json and PersonLookup.schema.v1.json exist and contain valid JSON Schema definitions with appropriate validation rules and data model bindings for their respective lookup components.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the schema files exist and contain valid JSON schema
for schema in "OrganisationLookup.schema.v1.json" "PersonLookup.schema.v1.json"; do
  echo "Checking schema: $schema"
  fd -t f "$schema" -x cat {} | jq 'has("$schema") and has("type") and has("properties")'
done

Length of output: 611


Script:

#!/bin/bash
# Display the content of schema files
for schema in "OrganisationLookup.schema.v1.json" "PersonLookup.schema.v1.json"; do
  echo "=== Content of $schema ==="
  fd -t f "$schema" -x cat {}
done

Length of output: 13346

frontend/packages/shared/src/types/ComponentSpecificConfig.ts (1)

343-346: LGTM! Component configurations follow the established pattern.

The OrganisationLookup and PersonLookup component configurations are correctly integrated into the ComponentSpecificConfig type, following the same pattern as other form components.

Also applies to: 354-357

frontend/packages/ux-editor/src/data/formItemConfig.ts (1)

593-599: LGTM! Components are correctly registered.

The OrganisationLookup and PersonLookup components are appropriately added to the form category.

frontend/packages/ux-editor/src/testing/schemas/json/component/PersonLookup.schema.v1.json (3)

1-3: LGTM! Schema metadata is properly defined.

The schema correctly specifies both the canonical URI and JSON Schema version.


146-149: LGTM! Required fields are properly defined.

The schema correctly enforces the minimum required properties for the component to function.


136-145: Review PII handling in data model bindings.

The component handles sensitive personal information (SSN and name). Ensure that:

  1. The data is properly encrypted in transit and at rest
  2. The component implements proper access controls
  3. The data is masked appropriately in the UI

Let's check for security measures in related files:

Comment on lines +17 to +29
[Fact]
public async Task Post_PersonLookup_Ok()
{
string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/person";
using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData);
httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet=");

using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

string responseBody = await response.Content.ReadAsStringAsync();
Assert.Contains("Test T. Testesen", responseBody);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add test coverage for error scenarios.

The current test only covers the successful case. Consider adding tests for:

  • Invalid request parameters
  • Missing referrer header
  • Error responses from the lookup service

Example test case:

[Fact]
public async Task Post_PersonLookup_MissingReferrer_ReturnsBadRequest()
{
    string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/person";
    using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData);
    // No referrer header set

    using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
    Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}

Comment on lines +18 to +31
[Fact]
public async Task Get_OrganisationLookup_Ok()
{
string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/organisation/{organisationNumber}";
using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData);
httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet=");

using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

string responseBody = await response.Content.ReadAsStringAsync();
Assert.Contains(organisationNumber, responseBody);
Assert.Contains("Test AS", responseBody);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance test coverage with additional scenarios.

Similar to PersonLookupTests, consider adding tests for:

  • Invalid organisation numbers (e.g., non-numeric, wrong length)
  • Missing referrer header
  • Error responses

Example test case:

[Theory]
[InlineData("12345")]  // Too short
[InlineData("1234567890")] // Too long
[InlineData("abcdefghi")] // Non-numeric
public async Task Get_OrganisationLookup_InvalidNumber_ReturnsBadRequest(string invalidNumber)
{
    string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/organisation/{invalidNumber}";
    using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData);
    httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet=");

    using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
    Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}

Comment on lines +694 to +707
/// <summary>
/// Action for mocking the POST method for person lookup for v4 apps
/// </summary>
/// <param name="org">The org</param>
/// <param name="app">The app</param>
/// <returns></returns>
[HttpPost]
[Route("api/v1/lookup/person")]
public IActionResult PersonLookup(string org, string app)
{
string mockSsn = "12345678912";
string lookupResponse = $"{{\"success\":true,\"personDetails\":{{\"ssn\":\"{mockSsn}\",\"name\":\"Test T. Testesen (preview)\", \"lastName\":\"Testesen (preview)\"}}}}";
return Ok(lookupResponse);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance the person lookup endpoint with request validation and error handling.

Consider the following improvements:

  1. Add request body model and validation
  2. Return more realistic mock data
  3. Add error handling
  4. Document the expected request format
+/// <summary>
+/// Request model for person lookup
+/// </summary>
+public class PersonLookupRequest
+{
+    public string SearchTerm { get; set; }
+    public string SearchType { get; set; } // e.g., "ssn", "name"
+}

 [HttpPost]
 [Route("api/v1/lookup/person")]
-public IActionResult PersonLookup(string org, string app)
+public IActionResult PersonLookup(string org, string app, [FromBody] PersonLookupRequest request)
 {
+    if (request == null || string.IsNullOrEmpty(request.SearchTerm))
+    {
+        return BadRequest(new { success = false, message = "Invalid request" });
+    }
+
     string mockSsn = "12345678912";
-    string lookupResponse = $"{{\"success\":true,\"personDetails\":{{\"ssn\":\"{mockSsn}\",\"name\":\"Test T. Testesen (preview)\", \"lastName\":\"Testesen (preview)\"}}}}";
+    string lookupResponse = $@"{{
+        ""success"": true,
+        ""personDetails"": {{
+            ""ssn"": ""{mockSsn}"",
+            ""firstName"": ""Test"",
+            ""middleName"": ""T."",
+            ""lastName"": ""Testesen (preview)"",
+            ""address"": {{
+                ""street"": ""Test Street 1"",
+                ""postalCode"": ""0123"",
+                ""city"": ""Oslo""
+            }}
+        }}
+    }}";
     return Ok(lookupResponse);
 }
📝 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.

Suggested change
/// <summary>
/// Action for mocking the POST method for person lookup for v4 apps
/// </summary>
/// <param name="org">The org</param>
/// <param name="app">The app</param>
/// <returns></returns>
[HttpPost]
[Route("api/v1/lookup/person")]
public IActionResult PersonLookup(string org, string app)
{
string mockSsn = "12345678912";
string lookupResponse = $"{{\"success\":true,\"personDetails\":{{\"ssn\":\"{mockSsn}\",\"name\":\"Test T. Testesen (preview)\", \"lastName\":\"Testesen (preview)\"}}}}";
return Ok(lookupResponse);
}
/// <summary>
/// Request model for person lookup
/// </summary>
public class PersonLookupRequest
{
public string SearchTerm { get; set; }
public string SearchType { get; set; } // e.g., "ssn", "name"
}
/// <summary>
/// Action for mocking the POST method for person lookup for v4 apps
/// </summary>
/// <param name="org">The org</param>
/// <param name="app">The app</param>
/// <returns></returns>
[HttpPost]
[Route("api/v1/lookup/person")]
public IActionResult PersonLookup(string org, string app, [FromBody] PersonLookupRequest request)
{
if (request == null || string.IsNullOrEmpty(request.SearchTerm))
{
return BadRequest(new { success = false, message = "Invalid request" });
}
string mockSsn = "12345678912";
string lookupResponse = $@"{{
""success"": true,
""personDetails"": {{
""ssn"": ""{mockSsn}"",
""firstName"": ""Test"",
""middleName"": ""T."",
""lastName"": ""Testesen (preview)"",
""address"": {{
""street"": ""Test Street 1"",
""postalCode"": ""0123"",
""city"": ""Oslo""
}}
}}
}}";
return Ok(lookupResponse);
}

Comment on lines +17 to +27
"grid": {
"properties": {
"xs": { "default": "auto", "$ref": "#/definitions/IGridSize" },
"sm": { "default": "auto", "$ref": "#/definitions/IGridSize" },
"md": { "default": "auto", "$ref": "#/definitions/IGridSize" },
"lg": { "default": "auto", "$ref": "#/definitions/IGridSize" },
"xl": { "default": "auto", "$ref": "#/definitions/IGridSize" },
"labelGrid": { "$ref": "#/definitions/IGridStyling" },
"innerGrid": { "$ref": "#/definitions/IGridStyling" }
}
},
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Fix incorrect grid definition references

The schema references #/definitions/IGridSize and #/definitions/IGridStyling, but these definitions don't exist. Instead, use:

  • Replace #/definitions/IGridSize with #/definitions/gridValue from common-defs.schema.v1.json
  • Replace #/definitions/IGridStyling with #/definitions/gridProps from common-defs.schema.v1.json
🔗 Analysis chain

Verify referenced grid definitions.

The schema references #/definitions/IGridSize and #/definitions/IGridStyling, but these definitions are not present in the current schema.

Let's verify the existence of these definitions:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if grid definitions exist in any related schema files

# Search for grid-related definitions in schema files
rg -l "IGridSize|IGridStyling" "frontend/packages/ux-editor/src/testing/schemas/json/"

Length of output: 4399


Script:

#!/bin/bash
# Check the content of Grid.schema.v1.json and look for definitions
cat "frontend/packages/ux-editor/src/testing/schemas/json/component/Grid.schema.v1.json"

# Also search for the actual definitions in all schema files
rg '"IGridSize"|"IGridStyling"' "frontend/packages/ux-editor/src/testing/schemas/json/" -A 5

Length of output: 5580


Script:

#!/bin/bash
# Look for potential base/common schema files
fd . "frontend/packages/ux-editor/src/testing/schemas/json/" -e json | grep -iE 'base|common|definitions|shared'

# Also check if there are any .json files directly in the schemas/json directory
ls -la frontend/packages/ux-editor/src/testing/schemas/json/

# Search for files containing "definitions" section
rg '"definitions":\s*{' "frontend/packages/ux-editor/src/testing/schemas/json/" -A 10

Length of output: 677


Script:

#!/bin/bash
# Check the contents of the common definitions file
cat "frontend/packages/ux-editor/src/testing/schemas/json/component/common-defs.schema.v1.json"

# Also try a simpler regex to find definitions
rg '"definitions"' "frontend/packages/ux-editor/src/testing/schemas/json/component/common-defs.schema.v1.json" -A 2

Length of output: 19391

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: 2

🧹 Nitpick comments (10)
backend/tests/Designer.Tests/Controllers/PreviewController/OrganisationLookupTests.cs (1)

12-12: Document the test data.

Add XML documentation to explain the significance of the organisation number used in tests.

-    protected const string organisationNumber = "123456789";
+    /// <summary>
+    /// Sample organisation number used for testing.
+    /// Format: 9 digits as per Norwegian organization number standard.
+    /// </summary>
+    protected const string organisationNumber = "123456789";
frontend/packages/shared/src/types/ComponentSpecificConfig.ts (2)

52-55: Add documentation for the DataModelBindingsOrganisationLookup type.

Consider adding JSDoc comments to document the purpose of this type and its fields.

+/**
+ * Data model bindings for the OrganisationLookup component.
+ * @property organisation_lookup_orgnr - Required field for storing the organization number
+ * @property organisation_lookup_name - Optional field for storing the organization name
+ */
 type DataModelBindingsOrganisationLookup = {
   organisation_lookup_orgnr: string | InternalBindingFormat;
   organisation_lookup_name?: string | InternalBindingFormat;
 };

57-60: Add documentation for the DataModelBindingsPersonLookup type.

Consider adding JSDoc comments to document the purpose of this type and its fields.

+/**
+ * Data model bindings for the PersonLookup component.
+ * @property person_lookup_ssn - Required field for storing the social security number
+ * @property person_lookup_name - Required field for storing the person's name
+ */
 type DataModelBindingsPersonLookup = {
   person_lookup_ssn: string | InternalBindingFormat;
   person_lookup_name: string | InternalBindingFormat;
 };
frontend/packages/ux-editor/src/data/formItemConfig.ts (2)

393-398: Enhance the OrganisationLookup configuration.

Consider the following improvements:

  1. Add default data model bindings to match other form components
  2. Use a more appropriate icon (e.g., GroupIcon or FileTextIcon) that better represents an organization lookup
 [ComponentType.OrganisationLookup]: {
   name: ComponentType.OrganisationLookup,
   itemType: LayoutItemType.Component,
-  defaultProperties: {},
+  defaultProperties: {
+    dataModelBindings: {
+      organisation_lookup_orgnr: '',
+      organisation_lookup_name: '',
+    },
+  },
-  icon: ShortTextIcon,
+  icon: GroupIcon,
 },

431-436: Enhance the PersonLookup configuration.

Consider the following improvements:

  1. Add default data model bindings to match other form components
  2. Use a more appropriate icon that better represents a person lookup
 [ComponentType.PersonLookup]: {
   name: ComponentType.PersonLookup,
   itemType: LayoutItemType.Component,
-  defaultProperties: {},
+  defaultProperties: {
+    dataModelBindings: {
+      person_lookup_ssn: '',
+      person_lookup_name: '',
+    },
+  },
-  icon: ShortTextIcon,
+  icon: TextIcon,
 },
backend/src/Designer/Controllers/PreviewController.cs (2)

679-692: Enhance the OrganisationLookup endpoint implementation.

Consider the following improvements:

  1. Add input validation for the organization number format
  2. Return more realistic mock data with additional fields
  3. Add error handling for invalid organization numbers
 [HttpGet]
 [Route("api/v1/lookup/organisation/{organisationNumber}")]
 public IActionResult OrganisationLookup(string org, string app, string organisationNumber)
 {
+    if (!IsValidOrganisationNumber(organisationNumber))
+    {
+        return BadRequest(new { success = false, message = "Invalid organisation number format" });
+    }
+
+    // Mock different responses based on the organization number
+    var mockData = new
+    {
+        success = true,
+        organisationDetails = new
+        {
+            orgNr = organisationNumber,
+            name = "Test AS (preview)",
+            type = "AS",
+            status = "Active",
+            address = "Test Street 1",
+            postalCode = "0123",
+            city = "Oslo"
+        }
+    };
-    string lookupResponse = $"{{\"success\":true,\"organisationDetails\":{{\"orgNr\":\"{organisationNumber}\",\"name\":\"Test AS (preview)\"}}}}";
-    return Ok(lookupResponse);
+    return Ok(mockData);
 }
+
+private static bool IsValidOrganisationNumber(string orgNr)
+{
+    return !string.IsNullOrEmpty(orgNr) && orgNr.Length == 9 && orgNr.All(char.IsDigit);
+}

694-707: Enhance the PersonLookup endpoint implementation.

Consider the following improvements:

  1. Add request body model and validation
  2. Return more realistic mock data with additional fields
  3. Add error handling for invalid SSNs
+public class PersonLookupRequest
+{
+    public string Ssn { get; set; }
+}
+
 [HttpPost]
 [Route("api/v1/lookup/person")]
-public IActionResult PersonLookup(string org, string app)
+public IActionResult PersonLookup(string org, string app, [FromBody] PersonLookupRequest request)
 {
-    string mockSsn = "12345678912";
-    string lookupResponse = $"{{\"success\":true,\"personDetails\":{{\"ssn\":\"{mockSsn}\",\"name\":\"Test T. Testesen (preview)\", \"lastName\":\"Testesen (preview)\"}}}}";
-    return Ok(lookupResponse);
+    if (request?.Ssn == null || !IsValidSsn(request.Ssn))
+    {
+        return BadRequest(new { success = false, message = "Invalid SSN format" });
+    }
+
+    var mockData = new
+    {
+        success = true,
+        personDetails = new
+        {
+            ssn = request.Ssn,
+            firstName = "Test",
+            middleName = "T.",
+            lastName = "Testesen",
+            name = "Test T. Testesen (preview)",
+            address = "Test Street 1",
+            postalCode = "0123",
+            city = "Oslo"
+        }
+    };
+    return Ok(mockData);
 }
+
+private static bool IsValidSsn(string ssn)
+{
+    return !string.IsNullOrEmpty(ssn) && ssn.Length == 11 && ssn.All(char.IsDigit);
+}
frontend/packages/ux-editor/src/testing/schemas/json/component/OrganisationLookup.schema.v1.json (1)

136-144: Add validation rules for organization number format.

Consider adding pattern validation for the organization number field to ensure it follows the correct format.

 "dataModelBindings": {
   "type": "object",
   "properties": {
-    "organisation_lookup_orgnr": { "$ref": "#/definitions/IRawDataModelBinding" },
+    "organisation_lookup_orgnr": {
+      "$ref": "#/definitions/IRawDataModelBinding",
+      "pattern": "^[0-9]{9}$",
+      "description": "Organization number must be exactly 9 digits"
+    },
     "organisation_lookup_name": { "$ref": "#/definitions/IRawDataModelBinding" }
   },
   "required": ["organisation_lookup_orgnr"],
   "additionalProperties": false
 }
frontend/packages/ux-editor/src/testing/schemas/json/component/PersonLookup.schema.v1.json (2)

8-9: Consider strengthening the ID pattern regex.

The current pattern ^[0-9a-zA-Z][0-9a-zA-Z-]*(-?[a-zA-Z]+|[a-zA-Z][0-9]+|-[0-9]{6,})$ allows for potentially confusing IDs. Consider:

  1. Adding a maximum length constraint
  2. Restricting consecutive hyphens
  3. Adding word boundaries to prevent confusion

51-54: Security warning needs more emphasis.

The readOnly description contains an important security warning about API manipulation. Consider:

  1. Moving this warning to a separate security-focused property
  2. Making it more prominent using stronger formatting
-      "description": "Boolean value or expression indicating if the component should be read only/disabled. Defaults to false. <br /> <i>Please note that even with read-only fields in components, it may currently be possible to update the field by modifying the request sent to the API or through a direct API call.<i/>",
+      "description": "Boolean value or expression indicating if the component should be read only/disabled. Defaults to false.",
+      "security_warning": "⚠️ SECURITY NOTICE: Read-only fields can still be modified through direct API calls or request manipulation. Server-side validation is required for security-critical fields.",
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7e0f720 and f3f95a7.

📒 Files selected for processing (9)
  • backend/src/Designer/Controllers/PreviewController.cs (1 hunks)
  • backend/tests/Designer.Tests/Controllers/PreviewController/OrganisationLookupTests.cs (1 hunks)
  • backend/tests/Designer.Tests/Controllers/PreviewController/PersonLookupTests.cs (1 hunks)
  • frontend/packages/shared/src/types/ComponentSpecificConfig.ts (2 hunks)
  • frontend/packages/shared/src/types/ComponentType.ts (1 hunks)
  • frontend/packages/ux-editor/src/data/formItemConfig.ts (4 hunks)
  • frontend/packages/ux-editor/src/testing/componentSchemaMocks.ts (2 hunks)
  • frontend/packages/ux-editor/src/testing/schemas/json/component/OrganisationLookup.schema.v1.json (1 hunks)
  • frontend/packages/ux-editor/src/testing/schemas/json/component/PersonLookup.schema.v1.json (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
frontend/packages/shared/src/types/ComponentSpecificConfig.ts

[error] 351-351: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 353-353: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🔇 Additional comments (9)
frontend/packages/shared/src/types/ComponentType.ts (1)

32-32: LGTM!

The new component types are correctly added and maintain the alphabetical ordering of the enum.

Also applies to: 37-37

frontend/packages/ux-editor/src/testing/componentSchemaMocks.ts (1)

31-31: LGTM! Verify schema files exist.

The new schema imports and component mappings are correctly added. Let's verify the referenced schema files exist.

Also applies to: 36-36, 78-78, 83-83

✅ Verification successful

Schema files verified successfully

Both schema files exist at the expected locations in the component schema directory.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify that the schema files exist
for schema in "OrganisationLookup.schema.v1.json" "PersonLookup.schema.v1.json"; do
  fd --type f "$schema" || echo "Missing schema file: $schema"
done

Length of output: 441

frontend/packages/shared/src/types/ComponentSpecificConfig.ts (1)

343-346: LGTM! The lookup components are well integrated.

The component configurations properly extend the base form component props and include the correct data model bindings.

Also applies to: 354-357

frontend/packages/ux-editor/src/data/formItemConfig.ts (1)

531-532: LGTM! Components are properly registered.

The lookup components are correctly added to both the schemaComponents array and the form category in allComponents.

Also applies to: 597-598

frontend/packages/ux-editor/src/testing/schemas/json/component/PersonLookup.schema.v1.json (5)

1-3: LGTM! Schema metadata follows best practices.

The schema uses proper versioning, HTTPS URL, and a stable JSON Schema specification version.


91-91: LGTM! Type constraint is properly defined.

The use of const ensures type safety for the PersonLookup component.


147-149: LGTM! Required fields are properly defined.

The schema correctly enforces the essential fields needed for the component to function.


136-145: Review PII handling for SSN data.

The schema handles sensitive personal information (SSN). Ensure:

  1. Compliance with privacy regulations
  2. Proper data encryption in transit and at rest
  3. Logging practices that avoid storing PII

92-135: Verify XSS protection for text resource bindings.

All text fields should be properly sanitized before rendering to prevent XSS attacks.

✅ Verification successful

Text resource bindings are secure by design

The expression system uses a strictly validated schema that:

  • Enforces type safety for all expressions
  • Limits string operations to safe transformations (concat, case conversion)
  • Handles text resources as plain text without HTML interpretation
  • Provides no mechanisms for raw HTML injection
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for XSS protection in text rendering components

# Look for sanitization functions or security measures
rg -A 5 'sanitize|escape|xss|security' frontend/packages/ux-editor/src

# Check if any HTML sanitization libraries are used
grep -r "DOMPurify\|sanitize-html\|xss" package.json package-lock.json

Length of output: 2040


Script:

#!/bin/bash
# Find and examine expression schema
echo "=== Expression Schema ==="
fd expression.schema.v1.json

# Look for text resource rendering
echo -e "\n=== Text Resource Rendering ==="
ast-grep --pattern 'textResourceBindings'

# Check expression evaluation
echo -e "\n=== Expression Evaluation ==="
rg -A 5 'evaluateExpression|renderText|textResource' frontend/packages/ux-editor/src

# Look for React components that might handle text rendering
echo -e "\n=== React Text Components ==="
ast-grep --pattern 'const $_ = ($props) => {
  $$$
  textResource
  $$$
}'

Length of output: 66697


Script:

#!/bin/bash
# Check expression schema definition
echo "=== Expression Schema Definition ==="
cat frontend/packages/ux-editor/src/testing/schemas/json/layout/expression.schema.v1.json

# Look for text rendering components and security measures
echo -e "\n=== Text Rendering & Security ==="
rg -A 5 'dangerouslySetInnerHTML|sanitize|xss|DOMPurify|escapeHtml' frontend/packages/ux-editor/src frontend/libs/studio-components/src

# Check for React components that render text
echo -e "\n=== Text Rendering Components ==="
ast-grep --pattern 'const $_ = ($props) => {
  $$$
  return (
    <$_>
      {$_}
    </$_>
  )
}'

Length of output: 18910

Comment on lines +17 to +29
[Fact]
public async Task Post_PersonLookup_Ok()
{
string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/person";
using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData);
httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet=");

using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

string responseBody = await response.Content.ReadAsStringAsync();
Assert.Contains("Test T. Testesen", responseBody);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance test coverage with additional test cases.

The current test only covers the happy path. Consider adding:

  • Test cases for invalid requests
  • Validation of the complete response structure
  • Edge cases (empty/invalid search criteria)
+ [Fact]
+ public async Task Post_PersonLookup_InvalidRequest_ReturnsBadRequest()
+ {
+     string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/person";
+     using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData);
+     httpRequestMessage.Content = new StringContent("invalid_request_body");
+ 
+     using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
+     Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +18 to +31
[Fact]
public async Task Get_OrganisationLookup_Ok()
{
string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/organisation/{organisationNumber}";
using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData);
httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet=");

using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

string responseBody = await response.Content.ReadAsStringAsync();
Assert.Contains(organisationNumber, responseBody);
Assert.Contains("Test AS", responseBody);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance test coverage with additional test cases.

The current test only covers the happy path. Consider adding:

  • Test with invalid organisation number format
  • Test with non-existent organisation number
  • Validation of complete response structure
+ [Fact]
+ public async Task Get_OrganisationLookup_InvalidFormat_ReturnsBadRequest()
+ {
+     string invalidOrgNumber = "invalid";
+     string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/organisation/{invalidOrgNumber}";
+     using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData);
+     httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet=");
+ 
+     using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
+     Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ }
📝 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.

Suggested change
[Fact]
public async Task Get_OrganisationLookup_Ok()
{
string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/organisation/{organisationNumber}";
using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData);
httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet=");
using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
string responseBody = await response.Content.ReadAsStringAsync();
Assert.Contains(organisationNumber, responseBody);
Assert.Contains("Test AS", responseBody);
}
[Fact]
public async Task Get_OrganisationLookup_Ok()
{
string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/organisation/{organisationNumber}";
using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData);
httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet=");
using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
string responseBody = await response.Content.ReadAsStringAsync();
Assert.Contains(organisationNumber, responseBody);
Assert.Contains("Test AS", responseBody);
}
[Fact]
public async Task Get_OrganisationLookup_InvalidFormat_ReturnsBadRequest()
{
string invalidOrgNumber = "invalid";
string dataPathWithData = $"{Org}/{AppV4}/api/v1/lookup/organisation/{invalidOrgNumber}";
using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData);
httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet=");
using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}

@nkylstad nkylstad changed the title Feat(UI editor)/add lookup components feat(ui-editor): add lookup components Feb 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/app-preview Area: Related to test and preview of apps that are developed in Altinn Studio. area/ui-editor Area: Related to the designer tool for assembling app UI in Altinn Studio. backend frontend quality/testing Tests that are missing, needs to be created or could be improved. solution/studio/designer Issues related to the Altinn Studio Designer solution. team/studio-domain2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant