From c30e87a82c944015b2e6f0ed424a0f5e724eec89 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Fri, 24 Jan 2025 11:55:50 -0500 Subject: [PATCH] [Tags] Expose TagsClient and utils on SOTagging server (#208109) Fixes #206948 ## Summary Adds TagsClient and utility methods on SavedObjectsTagging server plugin. This will unblock the Dashboard HTTP API handling tag references rather than requiring consumers to provide references. ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [x] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Identify risks Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss. Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging. - [ ] [See some risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) - [ ] ... --- .../plugins/shared/dashboard/kibana.jsonc | 3 +- .../epm/kibana/assets/saved_object.test.ts | 3 + .../saved_objects_tagging/common/index.ts | 9 ++ .../common/references.test.ts | 86 ++++++++++++++++++- .../common/references.ts | 49 ++++++++++- .../public/components/connected/tag_list.tsx | 2 +- .../ui_api/convert_name_to_reference.ts | 2 +- .../ui_api/get_table_column_definition.tsx | 3 +- .../public/ui_api/index.ts | 6 +- .../public/utils.test.ts | 83 +----------------- .../saved_objects_tagging/public/utils.ts | 51 ----------- .../saved_objects_tagging/server/mocks.ts | 3 + .../server/plugin.test.ts | 3 + .../saved_objects_tagging/server/plugin.ts | 8 +- .../saved_objects_tagging/server/types.ts | 12 ++- 15 files changed, 177 insertions(+), 146 deletions(-) diff --git a/src/platform/plugins/shared/dashboard/kibana.jsonc b/src/platform/plugins/shared/dashboard/kibana.jsonc index 9d47ab95c8872..d5ba75b11db10 100644 --- a/src/platform/plugins/shared/dashboard/kibana.jsonc +++ b/src/platform/plugins/shared/dashboard/kibana.jsonc @@ -35,6 +35,7 @@ "home", "spaces", "savedObjectsTaggingOss", + "savedObjectsTagging", "screenshotMode", "usageCollection", "taskManager", @@ -51,4 +52,4 @@ "savedObjects" ] } -} \ No newline at end of file +} diff --git a/x-pack/platform/plugins/shared/fleet/server/services/epm/kibana/assets/saved_object.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/epm/kibana/assets/saved_object.test.ts index 8ac5703265075..3a018819e2b19 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/epm/kibana/assets/saved_object.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/epm/kibana/assets/saved_object.test.ts @@ -19,6 +19,9 @@ describe('getSpaceAwareSaveobjectsClients', () => { const mockedSavedObjectTagging = { createInternalAssignmentService: jest.fn(), createTagClient: jest.fn(), + getTagsFromReferences: jest.fn(), + convertTagNameToId: jest.fn(), + replaceTagReferences: jest.fn(), }; const scoppedSoClient = savedObjectsClientMock.create(); diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/common/index.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/common/index.ts index d883ac78c7c05..12cd30e69fd7f 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/common/index.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/common/index.ts @@ -17,3 +17,12 @@ export { tagNameMaxLength, tagDescriptionMaxLength, } from './validation'; +export { + convertTagNameToId, + getObjectTags, + getTag, + getTagIdsFromReferences, + getTagsFromReferences, + replaceTagReferences, + tagIdToReference, +} from './references'; diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/common/references.test.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/common/references.test.ts index cc8093a5cebb2..d5e9d6c7b9f09 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/common/references.test.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/common/references.test.ts @@ -5,8 +5,16 @@ * 2.0. */ -import { SavedObjectReference } from '@kbn/core/types'; -import { tagIdToReference, replaceTagReferences, updateTagReferences } from './references'; +import type { SavedObject, SavedObjectReference } from '@kbn/core/server'; +import { + convertTagNameToId, + getObjectTags, + getTag, + getTagIdsFromReferences, + replaceTagReferences, + tagIdToReference, + updateTagReferences, +} from './references'; const ref = (type: string, id: string): SavedObjectReference => ({ id, @@ -16,6 +24,80 @@ const ref = (type: string, id: string): SavedObjectReference => ({ const tagRef = (id: string) => ref('tag', id); +const createObject = (refs: SavedObjectReference[]): SavedObject => { + return { + type: 'unkown', + id: 'irrelevant', + references: refs, + } as SavedObject; +}; + +const createTag = (id: string, name: string = id) => ({ + id, + name, + description: `desc ${id}`, + color: '#FFCC00', + managed: false, +}); + +const tag1 = createTag('id-1', 'name-1'); +const tag2 = createTag('id-2', 'name-2'); +const tag3 = createTag('id-3', 'name-3'); + +const allTags = [tag1, tag2, tag3]; + +describe('convertTagNameToId', () => { + it('returns the id for the given tag name', () => { + expect(convertTagNameToId('name-2', allTags)).toBe('id-2'); + }); + + it('returns undefined if no tag was found', () => { + expect(convertTagNameToId('name-4', allTags)).toBeUndefined(); + }); +}); + +describe('getObjectTags', () => { + it('returns the tags for the tag references of the object', () => { + const { tags } = getObjectTags( + createObject([tagRef('id-1'), ref('dashboard', 'dash-1'), tagRef('id-3')]), + allTags + ); + + expect(tags).toEqual([tag1, tag3]); + }); + + it('returns the missing references for tags that were not found', () => { + const missingRef = tagRef('missing-tag'); + const refs = [tagRef('id-1'), ref('dashboard', 'dash-1'), missingRef]; + const { tags, missingRefs } = getObjectTags(createObject(refs), allTags); + + expect(tags).toEqual([tag1]); + expect(missingRefs).toEqual([missingRef]); + }); +}); + +describe('getTag', () => { + it('returns the tag for the given id', () => { + expect(getTag('id-2', allTags)).toEqual(tag2); + }); + it('returns undefined if no tag was found', () => { + expect(getTag('id-4', allTags)).toBeUndefined(); + }); +}); + +describe('getTagIdsFromReferences', () => { + it('returns the tag ids from the given references', () => { + expect( + getTagIdsFromReferences([ + tagRef('tag-1'), + ref('dashboard', 'dash-1'), + tagRef('tag-2'), + ref('lens', 'lens-1'), + ]) + ).toEqual(['tag-1', 'tag-2']); + }); +}); + describe('tagIdToReference', () => { it('returns a reference for given tag id', () => { expect(tagIdToReference('some-tag-id')).toEqual({ diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/common/references.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/common/references.ts index 4328f006307a7..ec53584881d07 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/common/references.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/common/references.ts @@ -6,8 +6,15 @@ */ import { uniq, intersection } from 'lodash'; -import { SavedObjectReference } from '@kbn/core/types'; +import type { + SavedObject, + SavedObjectReference, + SavedObjectsFindOptionsReference, +} from '@kbn/core/server'; import { tagSavedObjectTypeName } from './constants'; +import { Tag } from './types'; + +type SavedObjectReferenceLike = SavedObjectReference | SavedObjectsFindOptionsReference; /** * Create a {@link SavedObjectReference | reference} for given tag id. @@ -64,3 +71,43 @@ export const updateTagReferences = ({ return [...nonTagReferences, ...newTagIds.map(tagIdToReference)]; }; + +export const getTagsFromReferences = (references: SavedObjectReference[], allTags: Tag[]) => { + const tagReferences = references.filter((ref) => ref.type === tagSavedObjectTypeName); + + const foundTags: Tag[] = []; + const missingRefs: SavedObjectReference[] = []; + + tagReferences.forEach((ref) => { + const found = allTags.find((tag) => tag.id === ref.id); + if (found) { + foundTags.push(found); + } else { + missingRefs.push(ref); + } + }); + + return { + tags: foundTags, + missingRefs, + }; +}; + +export const convertTagNameToId = (tagName: string, allTags: Tag[]): string | undefined => { + const found = allTags.find((tag) => tag.name.toLowerCase() === tagName.toLowerCase()); + return found?.id; +}; + +export const getObjectTags = ( + object: { references: SavedObject['references'] }, + allTags: Tag[] +) => { + return getTagsFromReferences(object.references, allTags); +}; + +export const getTag = (tagId: string, allTags: Tag[]): Tag | undefined => { + return allTags.find(({ id }) => id === tagId); +}; +export const getTagIdsFromReferences = (references: SavedObjectReferenceLike[]): string[] => { + return references.filter((ref) => ref.type === tagSavedObjectTypeName).map(({ id }) => id); +}; diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/connected/tag_list.tsx b/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/connected/tag_list.tsx index d3c65926fa00f..4ef099b3effd7 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/connected/tag_list.tsx +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/connected/tag_list.tsx @@ -10,10 +10,10 @@ import useObservable from 'react-use/lib/useObservable'; import type { SavedObjectReference } from '@kbn/core/types'; import type { TagListComponentProps } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { Tag, TagWithOptionalId } from '../../../common/types'; -import { getObjectTags } from '../../utils'; import { TagList } from '../base'; import type { ITagsCache } from '../../services'; import { byNameTagSorter } from '../../utils'; +import { getObjectTags } from '../../../common'; interface SavedObjectTagListProps { object: { references: SavedObjectReference[] }; diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/convert_name_to_reference.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/convert_name_to_reference.ts index 034005791d757..be6ab2cef1f1a 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/convert_name_to_reference.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/convert_name_to_reference.ts @@ -6,8 +6,8 @@ */ import { SavedObjectsTaggingApiUi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { convertTagNameToId } from '../../common'; import { ITagsCache } from '../services'; -import { convertTagNameToId } from '../utils'; export interface BuildConvertNameToReferenceOptions { cache: ITagsCache; diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/get_table_column_definition.tsx b/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/get_table_column_definition.tsx index ae9e96625eaa9..1f3a7769f44b3 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/get_table_column_definition.tsx +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/get_table_column_definition.tsx @@ -14,7 +14,8 @@ import { GetTableColumnDefinitionOptions, } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { ITagsCache } from '../services'; -import { getTagsFromReferences, byNameTagSorter } from '../utils'; +import { byNameTagSorter } from '../utils'; +import { getTagsFromReferences } from '../../common'; export interface BuildGetTableColumnDefinitionOptions { components: SavedObjectsTaggingApiUiComponent; diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/index.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/index.ts index 635e2e5af0440..c8e6284b1023b 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/index.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/public/ui_api/index.ts @@ -11,10 +11,10 @@ import { ITagsCache, ITagInternalClient } from '../services'; import { StartServices } from '../types'; import { getTagIdsFromReferences, - updateTagsReferences, + replaceTagReferences, convertTagNameToId, getTag, -} from '../utils'; +} from '../../common'; import { getComponents } from './components'; import { buildGetTableColumnDefinition } from './get_table_column_definition'; import { buildGetSearchBarFilter } from './get_search_bar_filter'; @@ -51,7 +51,7 @@ export const getUiApi = ({ convertNameToReference: buildConvertNameToReference({ cache }), getTagIdsFromReferences, getTagIdFromName: (tagName: string) => convertTagNameToId(tagName, cache.getState()), - updateTagsReferences, + updateTagsReferences: replaceTagReferences, getTag: (tagId: string) => getTag(tagId, cache.getState()), getTagList, }; diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/public/utils.test.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/public/utils.test.ts index c076b615bdda3..7a5f3fce12972 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/public/utils.test.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/public/utils.test.ts @@ -5,14 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectReference } from '@kbn/core/types'; -import { - getObjectTags, - convertTagNameToId, - byNameTagSorter, - getTagIdsFromReferences, - getTag, -} from './utils'; +import { byNameTagSorter } from './utils'; const createTag = (id: string, name: string = id) => ({ id, @@ -22,67 +15,6 @@ const createTag = (id: string, name: string = id) => ({ managed: false, }); -const ref = (type: string, id: string): SavedObjectReference => ({ - id, - type, - name: `${type}-ref-${id}`, -}); - -const tagRef = (id: string) => ref('tag', id); - -const createObject = (refs: SavedObjectReference[]): SavedObject => { - return { - type: 'unkown', - id: 'irrelevant', - references: refs, - } as SavedObject; -}; - -const tag1 = createTag('id-1', 'name-1'); -const tag2 = createTag('id-2', 'name-2'); -const tag3 = createTag('id-3', 'name-3'); - -const allTags = [tag1, tag2, tag3]; - -describe('getObjectTags', () => { - it('returns the tags for the tag references of the object', () => { - const { tags } = getObjectTags( - createObject([tagRef('id-1'), ref('dashboard', 'dash-1'), tagRef('id-3')]), - allTags - ); - - expect(tags).toEqual([tag1, tag3]); - }); - - it('returns the missing references for tags that were not found', () => { - const missingRef = tagRef('missing-tag'); - const refs = [tagRef('id-1'), ref('dashboard', 'dash-1'), missingRef]; - const { tags, missingRefs } = getObjectTags(createObject(refs), allTags); - - expect(tags).toEqual([tag1]); - expect(missingRefs).toEqual([missingRef]); - }); -}); - -describe('convertTagNameToId', () => { - it('returns the id for the given tag name', () => { - expect(convertTagNameToId('name-2', allTags)).toBe('id-2'); - }); - - it('returns undefined if no tag was found', () => { - expect(convertTagNameToId('name-4', allTags)).toBeUndefined(); - }); -}); - -describe('getTag', () => { - it('returns the tag for the given id', () => { - expect(getTag('id-2', allTags)).toEqual(tag2); - }); - it('returns undefined if no tag was found', () => { - expect(getTag('id-4', allTags)).toBeUndefined(); - }); -}); - describe('byNameTagSorter', () => { it('sorts tags by name', () => { const tags = [ @@ -97,16 +29,3 @@ describe('byNameTagSorter', () => { expect(tags.map(({ id }) => id)).toEqual(['id-2', 'id-1', 'id-4', 'id-3']); }); }); - -describe('getTagIdsFromReferences', () => { - it('returns the tag ids from the given references', () => { - expect( - getTagIdsFromReferences([ - tagRef('tag-1'), - ref('dashboard', 'dash-1'), - tagRef('tag-2'), - ref('lens', 'lens-1'), - ]) - ).toEqual(['tag-1', 'tag-2']); - }); -}); diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/public/utils.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/public/utils.ts index 14b47bfd8b815..971c404a1f8af 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/public/utils.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/public/utils.ts @@ -5,63 +5,12 @@ * 2.0. */ -import type { SavedObject, SavedObjectReference } from '@kbn/core/types'; -import type { SavedObjectsFindOptionsReference } from '@kbn/core/public'; import type { Tag } from '../common/types'; -import { tagSavedObjectTypeName } from '../common'; - -type SavedObjectReferenceLike = SavedObjectReference | SavedObjectsFindOptionsReference; - -export { - tagIdToReference, - replaceTagReferences as updateTagsReferences, -} from '../common/references'; - -export const getObjectTags = ( - object: { references: SavedObject['references'] }, - allTags: Tag[] -) => { - return getTagsFromReferences(object.references, allTags); -}; - -export const getTagsFromReferences = (references: SavedObjectReference[], allTags: Tag[]) => { - const tagReferences = references.filter((ref) => ref.type === tagSavedObjectTypeName); - - const foundTags: Tag[] = []; - const missingRefs: SavedObjectReference[] = []; - - tagReferences.forEach((ref) => { - const found = allTags.find((tag) => tag.id === ref.id); - if (found) { - foundTags.push(found); - } else { - missingRefs.push(ref); - } - }); - - return { - tags: foundTags, - missingRefs, - }; -}; - -export const convertTagNameToId = (tagName: string, allTags: Tag[]): string | undefined => { - const found = allTags.find((tag) => tag.name.toLowerCase() === tagName.toLowerCase()); - return found?.id; -}; export const byNameTagSorter = (tagA: Tag, tagB: Tag): number => { return tagA.name.localeCompare(tagB.name); }; -export const getTag = (tagId: string, allTags: Tag[]): Tag | undefined => { - return allTags.find(({ id }) => id === tagId); -}; - export const testSubjFriendly = (name: string) => { return name.replace(' ', '_'); }; - -export const getTagIdsFromReferences = (references: SavedObjectReferenceLike[]): string[] => { - return references.filter((ref) => ref.type === tagSavedObjectTypeName).map(({ id }) => id); -}; diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/server/mocks.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/server/mocks.ts index 6e1b456090dd4..daa7e441ebe69 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/server/mocks.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/server/mocks.ts @@ -13,6 +13,9 @@ const createStartMock = () => { const start: jest.Mocked = { createTagClient: jest.fn(), createInternalAssignmentService: jest.fn(), + getTagsFromReferences: jest.fn(), + convertTagNameToId: jest.fn(), + replaceTagReferences: jest.fn(), }; start.createTagClient.mockImplementation(() => tagsClientMock.create()); diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/server/plugin.test.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/server/plugin.test.ts index 78cd2cd1f1f95..cf38d3bd66c96 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/server/plugin.test.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/server/plugin.test.ts @@ -79,6 +79,9 @@ describe('SavedObjectTaggingPlugin', () => { expect(contract).toEqual({ createTagClient: expect.any(Function), createInternalAssignmentService: expect.any(Function), + convertTagNameToId: expect.any(Function), + getTagsFromReferences: expect.any(Function), + replaceTagReferences: expect.any(Function), }); }); }); diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/server/plugin.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/server/plugin.ts index e0e65c522b5eb..414ccbee7146b 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/server/plugin.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/server/plugin.ts @@ -21,6 +21,7 @@ import { TagsRequestHandlerContext } from './request_handler_context'; import { registerRoutes } from './routes'; import { createTagUsageCollector } from './usage'; import { TagsClient, AssignmentService } from './services'; +import { convertTagNameToId, getTagsFromReferences, replaceTagReferences } from '../common'; interface SetupDeps { features: FeaturesPluginSetup; @@ -36,7 +37,7 @@ export class SavedObjectTaggingPlugin implements Plugin<{}, SavedObjectTaggingStart, SetupDeps, StartDeps> { public setup( - { savedObjects, http, getStartServices }: CoreSetup, + { savedObjects, http, getStartServices }: CoreSetup, { features, usageCollection, security }: SetupDeps ) { savedObjects.registerType(tagType); @@ -69,7 +70,7 @@ export class SavedObjectTaggingPlugin return {}; } - public start(core: CoreStart, { security }: StartDeps) { + public start(core: CoreStart, { security }: StartDeps): SavedObjectTaggingStart { return { createTagClient: ({ client }: CreateTagClientOptions) => { return new TagsClient({ client }); @@ -82,6 +83,9 @@ export class SavedObjectTaggingPlugin internal: true, }); }, + convertTagNameToId, + getTagsFromReferences, + replaceTagReferences, }; } } diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/server/types.ts b/x-pack/platform/plugins/shared/saved_objects_tagging/server/types.ts index 156be38e79a0d..d2441d8e12282 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/server/types.ts +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/server/types.ts @@ -9,8 +9,9 @@ import type { IRouter, CustomRequestHandlerContext, SavedObjectsClientContract, + SavedObjectReference, } from '@kbn/core/server'; -import type { ITagsClient } from '../common/types'; +import type { ITagsClient, Tag } from '../common/types'; import type { IAssignmentService } from './services'; export interface ITagsRequestHandlerContext { @@ -47,6 +48,15 @@ export interface SavedObjectTaggingStart { createInternalAssignmentService: ( options: CreateTagAssignmentServiceOptions ) => IAssignmentService; + convertTagNameToId: (tagName: string, allTags: Tag[]) => string | undefined; + getTagsFromReferences: ( + references: SavedObjectReference[], + allTags: Tag[] + ) => { tags: Tag[]; missingRefs: SavedObjectReference[] }; + replaceTagReferences: ( + references: SavedObjectReference[], + newTagIds: string[] + ) => SavedObjectReference[]; } /**