Skip to content

Commit 33af728

Browse files
SuZhou-Joeruanyl
authored andcommitted
[Feature] Complied saved_objects create/find (opensearch-project#18)
* temp: save Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * feat: make create/find support workspaces Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * feat: extract management code Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * fix: type check Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * fix: build error Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * feat: enable workspaces on saved client server side Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * feat: some optimization Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * feat: extract management code Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * feat: merge fix Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * feat: optimize code Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * feat: reuse common function Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * feat: optimize code when create Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> * feat: remove useless test code Signed-off-by: SuZhoue-Joe <suzhou@amazon.com> --------- Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>
1 parent 1097a89 commit 33af728

File tree

19 files changed

+189
-16
lines changed

19 files changed

+189
-16
lines changed

src/core/public/saved_objects/saved_objects_client.ts

+30-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242

4343
import { SimpleSavedObject } from './simple_saved_object';
4444
import { HttpFetchOptions, HttpSetup } from '../http';
45+
import { WorkspacesStart } from '../workspace';
4546

4647
type SavedObjectsFindOptions = Omit<
4748
SavedObjectFindOptionsServer,
@@ -61,6 +62,7 @@ export interface SavedObjectsCreateOptions {
6162
/** {@inheritDoc SavedObjectsMigrationVersion} */
6263
migrationVersion?: SavedObjectsMigrationVersion;
6364
references?: SavedObjectReference[];
65+
workspaces?: string[];
6466
}
6567

6668
/**
@@ -183,6 +185,7 @@ const getObjectsToFetch = (queue: BatchQueueEntry[]): ObjectTypeAndId[] => {
183185
export class SavedObjectsClient {
184186
private http: HttpSetup;
185187
private batchQueue: BatchQueueEntry[];
188+
private currentWorkspaceId?: string;
186189

187190
/**
188191
* Throttled processing of get requests into bulk requests at 100ms interval
@@ -227,6 +230,15 @@ export class SavedObjectsClient {
227230
this.batchQueue = [];
228231
}
229232

233+
private async _getCurrentWorkspace(): Promise<string | null> {
234+
return this.currentWorkspaceId || null;
235+
}
236+
237+
public async setCurrentWorkspace(workspaceId: string): Promise<boolean> {
238+
this.currentWorkspaceId = workspaceId;
239+
return true;
240+
}
241+
230242
/**
231243
* Persists an object
232244
*
@@ -235,7 +247,7 @@ export class SavedObjectsClient {
235247
* @param options
236248
* @returns
237249
*/
238-
public create = <T = unknown>(
250+
public create = async <T = unknown>(
239251
type: string,
240252
attributes: T,
241253
options: SavedObjectsCreateOptions = {}
@@ -248,6 +260,7 @@ export class SavedObjectsClient {
248260
const query = {
249261
overwrite: options.overwrite,
250262
};
263+
const currentWorkspaceId = await this._getCurrentWorkspace();
251264

252265
const createRequest: Promise<SavedObject<T>> = this.savedObjectsFetch(path, {
253266
method: 'POST',
@@ -256,6 +269,11 @@ export class SavedObjectsClient {
256269
attributes,
257270
migrationVersion: options.migrationVersion,
258271
references: options.references,
272+
...(options.workspaces || currentWorkspaceId
273+
? {
274+
workspaces: options.workspaces || [currentWorkspaceId],
275+
}
276+
: {}),
259277
}),
260278
});
261279

@@ -328,7 +346,7 @@ export class SavedObjectsClient {
328346
* @property {object} [options.hasReference] - { type, id }
329347
* @returns A find result with objects matching the specified search.
330348
*/
331-
public find = <T = unknown>(
349+
public find = async <T = unknown>(
332350
options: SavedObjectsFindOptions
333351
): Promise<SavedObjectsFindResponsePublic<T>> => {
334352
const path = this.getPath(['_find']);
@@ -345,9 +363,18 @@ export class SavedObjectsClient {
345363
filter: 'filter',
346364
namespaces: 'namespaces',
347365
preference: 'preference',
366+
workspaces: 'workspaces',
348367
};
349368

350-
const renamedQuery = renameKeys<SavedObjectsFindOptions, any>(renameMap, options);
369+
const workspaces = [
370+
...(options.workspaces || [await this._getCurrentWorkspace()]),
371+
'public',
372+
].filter((item) => item);
373+
374+
const renamedQuery = renameKeys<SavedObjectsFindOptions, any>(renameMap, {
375+
...options,
376+
workspaces,
377+
});
351378
const query = pick.apply(null, [renamedQuery, ...Object.values<string>(renameMap)]) as Partial<
352379
Record<string, any>
353380
>;

src/core/server/saved_objects/export/get_sorted_objects_for_export.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ export interface SavedObjectsExportOptions {
6060
excludeExportDetails?: boolean;
6161
/** optional namespace to override the namespace used by the savedObjectsClient. */
6262
namespace?: string;
63+
/** optional workspaces to override the workspaces used by the savedObjectsClient. */
64+
workspaces?: string[];
6365
}
6466

6567
/**
@@ -87,13 +89,15 @@ async function fetchObjectsToExport({
8789
exportSizeLimit,
8890
savedObjectsClient,
8991
namespace,
92+
workspaces,
9093
}: {
9194
objects?: SavedObjectsExportOptions['objects'];
9295
types?: string[];
9396
search?: string;
9497
exportSizeLimit: number;
9598
savedObjectsClient: SavedObjectsClientContract;
9699
namespace?: string;
100+
workspaces?: string[];
97101
}) {
98102
if ((types?.length ?? 0) > 0 && (objects?.length ?? 0) > 0) {
99103
throw Boom.badRequest(`Can't specify both "types" and "objects" properties when exporting`);
@@ -105,7 +109,7 @@ async function fetchObjectsToExport({
105109
if (typeof search === 'string') {
106110
throw Boom.badRequest(`Can't specify both "search" and "objects" properties when exporting`);
107111
}
108-
const bulkGetResult = await savedObjectsClient.bulkGet(objects, { namespace });
112+
const bulkGetResult = await savedObjectsClient.bulkGet(objects, { namespace, workspaces });
109113
const erroredObjects = bulkGetResult.saved_objects.filter((obj) => !!obj.error);
110114
if (erroredObjects.length) {
111115
const err = Boom.badRequest();
@@ -121,6 +125,7 @@ async function fetchObjectsToExport({
121125
search,
122126
perPage: exportSizeLimit,
123127
namespaces: namespace ? [namespace] : undefined,
128+
workspaces,
124129
});
125130
if (findResponse.total > exportSizeLimit) {
126131
throw Boom.badRequest(`Can't export more than ${exportSizeLimit} objects`);
@@ -153,6 +158,7 @@ export async function exportSavedObjectsToStream({
153158
includeReferencesDeep = false,
154159
excludeExportDetails = false,
155160
namespace,
161+
workspaces,
156162
}: SavedObjectsExportOptions) {
157163
const rootObjects = await fetchObjectsToExport({
158164
types,
@@ -161,6 +167,7 @@ export async function exportSavedObjectsToStream({
161167
savedObjectsClient,
162168
exportSizeLimit,
163169
namespace,
170+
workspaces,
164171
});
165172
let exportedObjects: Array<SavedObject<unknown>> = [];
166173
let missingReferences: SavedObjectsExportResultDetails['missingReferences'] = [];

src/core/server/saved_objects/import/create_saved_objects.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ interface CreateSavedObjectsParams<T> {
3939
importIdMap: Map<string, { id?: string; omitOriginId?: boolean }>;
4040
namespace?: string;
4141
overwrite?: boolean;
42+
workspaces?: string[];
4243
}
4344
interface CreateSavedObjectsResult<T> {
4445
createdObjects: Array<CreatedObject<T>>;
@@ -56,6 +57,7 @@ export const createSavedObjects = async <T>({
5657
importIdMap,
5758
namespace,
5859
overwrite,
60+
workspaces,
5961
}: CreateSavedObjectsParams<T>): Promise<CreateSavedObjectsResult<T>> => {
6062
// filter out any objects that resulted in errors
6163
const errorSet = accumulatedErrors.reduce(
@@ -103,14 +105,15 @@ export const createSavedObjects = async <T>({
103105
const bulkCreateResponse = await savedObjectsClient.bulkCreate(objectsToCreate, {
104106
namespace,
105107
overwrite,
108+
workspaces,
106109
});
107110
expectedResults = bulkCreateResponse.saved_objects;
108111
}
109112

110113
// remap results to reflect the object IDs that were submitted for import
111114
// this ensures that consumers understand the results
112115
const remappedResults = expectedResults.map<CreatedObject<T>>((result) => {
113-
const { id } = objectIdMap.get(`${result.type}:${result.id}`)!;
116+
const { id } = objectIdMap.get(`${result.type}:${result.id}`) || ({} as SavedObject<T>);
114117
// also, include a `destinationId` field if the object create attempt was made with a different ID
115118
return { ...result, id, ...(id !== result.id && { destinationId: result.id }) };
116119
});

src/core/server/saved_objects/import/import_saved_objects.ts

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export async function importSavedObjectsFromStream({
5454
savedObjectsClient,
5555
typeRegistry,
5656
namespace,
57+
workspaces,
5758
}: SavedObjectsImportOptions): Promise<SavedObjectsImportResponse> {
5859
let errorAccumulator: SavedObjectsImportError[] = [];
5960
const supportedTypes = typeRegistry.getImportableAndExportableTypes().map((type) => type.name);
@@ -118,6 +119,7 @@ export async function importSavedObjectsFromStream({
118119
importIdMap,
119120
overwrite,
120121
namespace,
122+
workspaces,
121123
};
122124
const createSavedObjectsResult = await createSavedObjects(createSavedObjectsParams);
123125
errorAccumulator = [...errorAccumulator, ...createSavedObjectsResult.errors];

src/core/server/saved_objects/import/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ export interface SavedObjectsImportOptions {
187187
namespace?: string;
188188
/** If true, will create new copies of import objects, each with a random `id` and undefined `originId`. */
189189
createNewCopies: boolean;
190+
/** if specified, will import in given workspaces, else will import as global object */
191+
workspaces?: string[];
190192
}
191193

192194
/**

src/core/server/saved_objects/migrations/core/build_active_mappings.ts

+3
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ function defaultMapping(): IndexMapping {
175175
},
176176
},
177177
},
178+
workspaces: {
179+
type: 'keyword',
180+
},
178181
},
179182
};
180183
}

src/core/server/saved_objects/routes/bulk_create.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import { schema } from '@osd/config-schema';
3232
import { IRouter } from '../../http';
33+
import { formatWorkspaces, workspacesValidator } from './utils';
3334

3435
export const registerBulkCreateRoute = (router: IRouter) => {
3536
router.post(
@@ -38,6 +39,7 @@ export const registerBulkCreateRoute = (router: IRouter) => {
3839
validate: {
3940
query: schema.object({
4041
overwrite: schema.boolean({ defaultValue: false }),
42+
workspaces: workspacesValidator,
4143
}),
4244
body: schema.arrayOf(
4345
schema.object({
@@ -62,7 +64,11 @@ export const registerBulkCreateRoute = (router: IRouter) => {
6264
},
6365
router.handleLegacyErrors(async (context, req, res) => {
6466
const { overwrite } = req.query;
65-
const result = await context.core.savedObjects.client.bulkCreate(req.body, { overwrite });
67+
const workspaces = formatWorkspaces(req.query.workspaces);
68+
const result = await context.core.savedObjects.client.bulkCreate(req.body, {
69+
overwrite,
70+
workspaces,
71+
});
6672
return res.ok({ body: result });
6773
})
6874
);

src/core/server/saved_objects/routes/create.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,23 @@ export const registerCreateRoute = (router: IRouter) => {
5656
)
5757
),
5858
initialNamespaces: schema.maybe(schema.arrayOf(schema.string(), { minSize: 1 })),
59+
workspaces: schema.maybe(schema.arrayOf(schema.string(), { minSize: 1 })),
5960
}),
6061
},
6162
},
6263
router.handleLegacyErrors(async (context, req, res) => {
6364
const { type, id } = req.params;
6465
const { overwrite } = req.query;
65-
const { attributes, migrationVersion, references, initialNamespaces } = req.body;
66+
const { attributes, migrationVersion, references, initialNamespaces, workspaces } = req.body;
6667

67-
const options = { id, overwrite, migrationVersion, references, initialNamespaces };
68+
const options = {
69+
id,
70+
overwrite,
71+
migrationVersion,
72+
references,
73+
initialNamespaces,
74+
workspaces,
75+
};
6876
const result = await context.core.savedObjects.client.create(type, attributes, options);
6977
return res.ok({ body: result });
7078
})

src/core/server/saved_objects/routes/export.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,20 @@ export const registerExportRoute = (router: IRouter, config: SavedObjectConfig)
5757
search: schema.maybe(schema.string()),
5858
includeReferencesDeep: schema.boolean({ defaultValue: false }),
5959
excludeExportDetails: schema.boolean({ defaultValue: false }),
60+
workspaces: schema.maybe(schema.arrayOf(schema.string())),
6061
}),
6162
},
6263
},
6364
router.handleLegacyErrors(async (context, req, res) => {
6465
const savedObjectsClient = context.core.savedObjects.client;
65-
const { type, objects, search, excludeExportDetails, includeReferencesDeep } = req.body;
66+
const {
67+
type,
68+
objects,
69+
search,
70+
excludeExportDetails,
71+
includeReferencesDeep,
72+
workspaces,
73+
} = req.body;
6674
const types = typeof type === 'string' ? [type] : type;
6775

6876
// need to access the registry for type validation, can't use the schema for this
@@ -98,6 +106,7 @@ export const registerExportRoute = (router: IRouter, config: SavedObjectConfig)
98106
exportSizeLimit: maxImportExportSize,
99107
includeReferencesDeep,
100108
excludeExportDetails,
109+
workspaces,
101110
});
102111

103112
const docsToExport: string[] = await createPromiseFromStreams([

src/core/server/saved_objects/routes/find.ts

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import { schema } from '@osd/config-schema';
3232
import { IRouter } from '../../http';
33+
import { formatWorkspaces, workspacesValidator } from './utils';
3334

3435
export const registerFindRoute = (router: IRouter) => {
3536
router.get(
@@ -59,6 +60,7 @@ export const registerFindRoute = (router: IRouter) => {
5960
namespaces: schema.maybe(
6061
schema.oneOf([schema.string(), schema.arrayOf(schema.string())])
6162
),
63+
workspaces: workspacesValidator,
6264
}),
6365
},
6466
},
@@ -67,6 +69,7 @@ export const registerFindRoute = (router: IRouter) => {
6769

6870
const namespaces =
6971
typeof req.query.namespaces === 'string' ? [req.query.namespaces] : req.query.namespaces;
72+
const workspaces = formatWorkspaces(query.workspaces);
7073

7174
const result = await context.core.savedObjects.client.find({
7275
perPage: query.per_page,
@@ -81,6 +84,7 @@ export const registerFindRoute = (router: IRouter) => {
8184
fields: typeof query.fields === 'string' ? [query.fields] : query.fields,
8285
filter: query.filter,
8386
namespaces,
87+
workspaces,
8488
});
8589

8690
return res.ok({ body: result });

src/core/server/saved_objects/routes/import.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { schema } from '@osd/config-schema';
3434
import { IRouter } from '../../http';
3535
import { importSavedObjectsFromStream } from '../import';
3636
import { SavedObjectConfig } from '../saved_objects_config';
37-
import { createSavedObjectsStreamFromNdJson } from './utils';
37+
import { createSavedObjectsStreamFromNdJson, formatWorkspaces, workspacesValidator } from './utils';
3838

3939
interface FileStream extends Readable {
4040
hapi: {
@@ -60,6 +60,7 @@ export const registerImportRoute = (router: IRouter, config: SavedObjectConfig)
6060
{
6161
overwrite: schema.boolean({ defaultValue: false }),
6262
createNewCopies: schema.boolean({ defaultValue: false }),
63+
workspaces: workspacesValidator,
6364
},
6465
{
6566
validate: (object) => {
@@ -91,13 +92,16 @@ export const registerImportRoute = (router: IRouter, config: SavedObjectConfig)
9192
});
9293
}
9394

95+
const workspaces = formatWorkspaces(req.query.workspaces);
96+
9497
const result = await importSavedObjectsFromStream({
9598
savedObjectsClient: context.core.savedObjects.client,
9699
typeRegistry: context.core.savedObjects.typeRegistry,
97100
readStream,
98101
objectLimit: maxImportExportSize,
99102
overwrite,
100103
createNewCopies,
104+
workspaces,
101105
});
102106

103107
return res.ok({ body: result });

0 commit comments

Comments
 (0)