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

[Cases] Add custom fields: List type #194236

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

Conversation

Zacqary
Copy link
Contributor

@Zacqary Zacqary commented Sep 26, 2024

Summary

Closes #176491

Adds the List custom field type to Cases. Also makes some changes to the custom field configuration type system to handle the new features added in this field type.

The List field type creates a <select> dropdown with a set of options determined by the custom field's configuration. Options are defined as { key: UUID, label: String }.

When the user selects a value, it is saved to the case with the schema { key: Custom Field UUID, value: Option UUID }. The option's label is fetched at runtime by the UI from the custom field configuration, and displayed. This is a deviation from the research issue proposal, which specced a schema of { key: ${Field UUID}.{Option UUID}, label: Option Label }. Engineering this turned out to require major changes to the form UI library and the API transformation libraries, and it's a less drastic change to simply convert the key to a label when displaying the field.

Option labels can be renamed for free using this new schema, since the UUID is the only thing stored in case saved objects. We don't need to create a new issue to add renaming support.

This PR also allows List fields to be filtered. This required a change from using static filterOptions in custom field configurations to a dynamic getFilterOptions function. The Toggle field was updated to use this new dynamic function.

Screenshot 2024-09-26 at 5 33 26 PM Screenshot 2024-09-26 at 5 32 22 PM

Known Issues

Due to elastic/eui#8027 drag-and-drop on the new <OptionsField> doesn't correctly render the current draggable element inside the flyout. EUI is aiming to fix this before 8.16.

Screenshot 2024-09-26 at 5 42 38 PM

Checklist

@Zacqary Zacqary added release_note:enhancement Team:ResponseOps Label for the ResponseOps team (formerly the Cases and Alerting teams) v9.0.0 Feature:Cases Cases feature v8.16.0 backport:version Backport to applied version labels labels Sep 26, 2024
@obltmachine
Copy link

🤖 GitHub comments

Expand to view the GitHub comments

Just comment with:

  • /oblt-deploy : Deploy a Kibana instance using the Observability test environments.
  • run docs-build : Re-trigger the docs validation. (use unformatted text in the comment!)

@Zacqary
Copy link
Contributor Author

Zacqary commented Oct 2, 2024

/ci

@Zacqary Zacqary marked this pull request as ready for review October 2, 2024 14:41
@Zacqary Zacqary requested a review from a team as a code owner October 2, 2024 14:41
@elasticmachine
Copy link
Contributor

Pinging @elastic/response-ops (Team:ResponseOps)

@elasticmachine
Copy link
Contributor

Pinging @elastic/response-ops-cases (Feature:Cases)

@Zacqary Zacqary enabled auto-merge (squash) October 2, 2024 14:41
@kibana-ci
Copy link
Collaborator

💛 Build succeeded, but was flaky

Failed CI Steps

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
cases 812 822 +10

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
cases 491.4KB 494.3KB +2.8KB

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
cases 151.1KB 160.8KB +9.7KB

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

rt.strict({ type: CustomFieldListTypeRt }),
CustomFieldConfigurationWithoutTypeRt,
rt.strict({
options: rt.array(ListCustomFieldOptionRt),
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we limit the number of options? @cnasikas

Suggested change
options: rt.array(ListCustomFieldOptionRt),
options: limitedArraySchema(ListCustomFieldOptionRt),

They can easily get out of hand.
Screenshot 2024-10-14 at 10 56 17

<EuiFlexItem>
<EuiButtonEmpty
iconType={'plusInCircle'}
onClick={onAddOption}
Copy link
Contributor

Choose a reason for hiding this comment

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

Testing this form one thing I missed from a user perspective was pressing Enter to add a new option.

That way I can type an option, press enter, type another, press enter, type another, etc

Instead of type, mouse, type, mouse.

UX-wise I really missed that option.

Copy link
Contributor

@adcoelho adcoelho left a comment

Choose a reason for hiding this comment

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

I have a bug with the following scenario:

  1. Created a List custom field with two options(b and c).
  2. Created a case and set the custom field value to be b.
  3. Went back to the settings and deleted the option b.
  4. Now in the case the custom field has no value but I cannot edit it to set c. (See video.)
Screen.Recording.2024-10-14.at.11.25.02.mov

@Zacqary
Copy link
Contributor Author

Zacqary commented Oct 14, 2024

@adcoelho Feedback addressed. Added a 10 item limit to options on the UI side and the API side. Bug fixed as well.

@jcger
Copy link
Contributor

jcger commented Oct 16, 2024

Found a couple of issues:

1. Moving an empty new option causes another one to be created

Screen recording
Screenshot.mov

2. The first element of every list seems to be stored with uuid 0, here is the output of two lists I created. Maybe it's ok? I just saw that it's intentional

Query (dunno how to search by list type directly):

GET .kibana_alerting_cases/_search
{
  "query": {
    "wildcard": {
      "cases-configure.owner": {
        "value": "*"
      }
    }
  }
}
Output
"customFields": [
              {
                "type": "list",
                "key": "6611d94a-a813-4d0f-9dae-0070e6208ff1",
                "label": "MyList",
                "required": false,
                "options": [
                  {
                    "key": "00000000-0000-0000-0000-000000000000", // HERE <------------
                    "label": "a"
                  },
                  {
                    "key": "9ff0b4a2-cfee-42fa-9374-37d0c2654f12",
                    "label": "b"
                  },
                  {
                    "key": "33b8be32-4ffc-4b39-bea7-554396017b51",
                    "label": "c"
                  },
                  {
                    "key": "832187c8-8c61-4953-9d78-fd942d8fc738",
                    "label": "d"
                  }
                ],
                "defaultValue": "9ff0b4a2-cfee-42fa-9374-37d0c2654f12"
              },
              {
                "type": "list",
                "key": "9a716a2c-3922-4a6a-a066-f883aa048c71",
                "label": "MySecondList",
                "required": false,
                "options": [
                  {
                    "key": "00000000-0000-0000-0000-000000000000", // HERE <------------
                    "label": "1"
                  },
                  {
                    "key": "f1033a37-7bd0-45df-883e-ab32f95a3b46",
                    "label": "2"
                  },
                  {
                    "key": "02eaef55-95ad-4486-9dd4-99b8abad4d77",
                    "label": "3"
                  },
                  {
                    "key": "d8a6fd18-1b58-4f01-864d-99e13bcbcd92",
                    "label": "4"
                  }
                ]
              }
            ]

Copy link
Contributor

@js-jankisalvi js-jankisalvi left a comment

Choose a reason for hiding this comment

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

Great work!! 🎉

Found an issue in continuation to what @adcoelho found while deleting an option. Once the option which was selected as defaultValue is deleted, the next option is set as default in custom field.
However it is not visible when case is created.

See video:
List.cf.delete.mov

value: false,
},
],
// await waitFor(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

forgotten comment, if you are 100% you want to do this it should be removed, right? I personally would leave it as it was just because I'm to afraid of flakiness in CI. There is at least two more places where this happens, I don't want to spam with "same here"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching that

))}
</EuiDroppable>
</EuiDragDropContext>
{maxOptions && currentOptions.length < maxOptions && (
Copy link
Contributor

Choose a reason for hiding this comment

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

should we test this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes probably, just added this feature and neglected to test, fixing now

Copy link
Member

@cnasikas cnasikas left a comment

Choose a reason for hiding this comment

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

In the research issue, we went through all the available options and we picked the one that satisfies all of the criteria: filtering, searching, sorting, renaming, and deletion.

Key in Cases SO Value in Cases SO Filtering Searching Sorting OFTB Renaming OFTB Deletion
Custom field key (UUID) Option key (UUID)
Custom field key + Option key Actual value

You cannot sort or search in ES because the SO will have the UUID and not the actual value. For the second option in the above table, we can satisfy renaming or deletion but we need to use the task manager which requires extra work as described in the research issue.

Engineering this turned out to require major changes to the form UI library and the API transformation libraries, and it's a less drastic change to simply convert the key to a label when displaying the field.

As mentioned in #176491 there is no need to make changes in the form UI library. The library supports serializer and deserializer where we can convert the form (UI) data to the schema expected by the backend and vice-versa. Also, the transformer we have in place in the CasesService can transform the data from what is expected by the cases client and the cases SO.

@elasticmachine
Copy link
Contributor

elasticmachine commented Oct 17, 2024

💔 Build Failed

Failed CI Steps

Test Failures

  • [job] [logs] Jest Integration Tests #1 / incompatible_cluster_routing_allocation retries the INIT action with a descriptive message when cluster settings are incompatible
  • [job] [logs] Jest Integration Tests #1 / incompatible_cluster_routing_allocation retries the INIT action with a descriptive message when cluster settings are incompatible
  • [job] [logs] Jest Integration Tests #1 / migration v2 - read batch size does not reduce the read batchSize in half if no batches exceeded maxReadBatchSizeBytes
  • [job] [logs] Jest Integration Tests #1 / migration v2 - read batch size does not reduce the read batchSize in half if no batches exceeded maxReadBatchSizeBytes
  • [job] [logs] Jest Integration Tests #1 / migration v2 - read batch size reduces the read batchSize in half if a batch exceeds maxReadBatchSizeBytes
  • [job] [logs] Jest Integration Tests #1 / migration v2 - read batch size reduces the read batchSize in half if a batch exceeds maxReadBatchSizeBytes
  • [job] [logs] Jest Integration Tests #1 / migration v2 migrates saved objects normally with multiple ES nodes
  • [job] [logs] Jest Integration Tests #1 / migration v2 migrates saved objects normally with multiple ES nodes

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
cases 812 822 +10

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
cases 491.9KB 494.8KB +2.8KB

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
cases 151.2KB 161.1KB +9.9KB

History

@Zacqary
Copy link
Contributor Author

Zacqary commented Oct 17, 2024

I'm able to get as far as transforming the key/value pair to the dot notation in x-pack/plugins/cases/server/services/cases/transform.ts, but this throws a 500 error of Invalid value "[the supplied value label]" supplied to "customFields,value".

I imagine this might have something to do with saved object schemas but I'm unsure. Will keep investigating

@Zacqary
Copy link
Contributor Author

Zacqary commented Oct 18, 2024

Schema is getting saved in the form successfully but deserializing it into the View and Edit components don't seem to be working properly. Trying to figure that out, it's not calling the deserializer where I expected it to but still somehow transforming the saved object value I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:version Backport to applied version labels Feature:Cases Cases feature release_note:enhancement Team:ResponseOps Label for the ResponseOps team (formerly the Cases and Alerting teams) v8.16.0 v9.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Cases] Custom fields: List type
8 participants