# \[7.15\] Kibana plugin development - Import saved objects to specific space

**URL:** <https://discuss.elastic.co/t/7-15-kibana-plugin-development-import-saved-objects-to-specific-space/293869>\
**Category:** Kibana\
**Created:** [January 10, 2022, 9:41am UTC](https://discuss.elastic.co/t/7-15-kibana-plugin-development-import-saved-objects-to-specific-space/293869 "2022-01-10T09:41:13Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Fabio-sama](https://avatars.discourse-cdn.com/v4/letter/f/b9e5f3/32.png) [@Fabio-sama](https://discuss.elastic.co/u/Fabio-sama)\
**Post date:** [January 10, 2022, 9:41am UTC](https://discuss.elastic.co/t/7-15-kibana-plugin-development-import-saved-objects-to-specific-space/293869/1 "2022-01-10T09:41:13Z")

</div>

Hi there,

my team and I are developing a custom Kibana plugin and during the initialization phase we'd like to import some custom Saved Objects into a new space.

Basically, we managed to create the space during the initialization phase, requiring the `spaces` x-pack plugin in the `kibana.json` file and adding the following lines:

```auto
let namespacesResponse = await this.externalPluginsService.getExternalPlugins().spaces?.spacesService?.createSpacesClient(request).getAll();
const namespaces = namespacesResponse?.map((namespace: GetSpaceResult) => namespace.id) as string[];
if (!namespaces.includes('my_custom_space')) await this.externalPluginsService.getExternalPlugins().spaces?.spacesService?.createSpacesClient(request).create(INIT_QUERIES.INIT_EPI_SPACE);

```

Now we'd like to import what's inside a ndjson file into that new space, basically the equivalent of switching to that space in the Kibana console, go to Management\>Saved Object and import a given ndjson file or of making the [following cURL](https://www.elastic.co/guide/en/kibana/master/saved-objects-api-import.html):

```auto
curl -XPOST "http://localhost:5601/ayo/s/my_custom_space/api/saved_objects/_import" -H "kbn-xsrf: true" --form file=@./server/services/my_custom_savedobjects.ndjson -u elastic

```

Is there a straightforward way to achieve such a result?

What we had to do was to install a new module ([fs-ndjson](https://www.npmjs.com/package/fs-ndjson)), use its readFile method to read from a given path, and use the getImporter method as follows:

```auto
const importer = await context.core.savedObjects.getImporter(context.core.savedObjects.getClient());
importer.import({
    overwrite: true,
    createNewCopies: false,
    readStream: this.createReadableStreamFromArray(epi_array)
})

```

Unfortunately, it seems to only work with the Default space. In fact, if we try to add the `namespace` option to the `import` method (defined as optional in the `SavedObjectsImportOptions` interface), it always returns the **Spaces currently determines the namespaces** error, thrown by the `throwErrorIfNamespaceSpecified` of the `checkConflicts` function, defined (in the _x-pack/plugins/spaces/server/saved\_objects/spaces\_saved\_objects\_client.ts_ file) as follows

```auto
/**
* Check what conflicts will result when creating a given array of saved objects. This includes "unresolvable conflicts", which are
* multi-namespace objects that exist in a different namespace; such conflicts cannot be resolved/overwritten.
*
* @param objects
* @param options
*/
public async checkConflicts(
objects: SavedObjectsCheckConflictsObject[] = [],
options: SavedObjectsBaseOptions = {}
) {
throwErrorIfNamespaceSpecified(options);

return await this.client.checkConflicts(objects, {
    ...options,
    namespace: spaceIdToNamespace(this.spaceId),
});
}

```

Why is there a `namespace` option - which perfectly makes sense in order to pick the space you want to import your saved objects to - but you cannot use it without throwing that error? What are we doing wrong?

Thank you in advance!

---

<div class="post-metadata">

**Author:** ![Fabio-sama](https://avatars.discourse-cdn.com/v4/letter/f/b9e5f3/32.png) [@Fabio-sama](https://discuss.elastic.co/u/Fabio-sama)\
**Post date:** [January 10, 2022, 10:23am UTC](https://discuss.elastic.co/t/7-15-kibana-plugin-development-import-saved-objects-to-specific-space/293869/2 "2022-01-10T10:23:45Z")

</div>

**UPDATES**

It seems I managed to make it work replacing this

```auto
const importer = await context.core.savedObjects.getImporter(context.core.savedObjects.getClient());

```

with this

```auto
const importer = await context.core.savedObjects.getImporter(context.core.savedObjects.getClient({excludedWrappers: ['spaces']}));

```

that is adding the excludeWrappers option in the getClient method. That way the wrapper that raises an error if the namespace is specified is ignored and I can then proceeed with the import with a specific space as follows:

```auto
importer.import({
    overwrite: true,
    createNewCopies: false,
    namespace: 'my_custom_space',
    readStream: this.createReadableStreamFromArray(epi_array)
})

```

It seems to work flawlessy. Is it the correct way to achieve this result?

Also, when in dev mode I can read the ndjson file using the relative path, as follows:

```auto
await fsNdjson.readFile('plugins/my_plugin_folder/server/services/my_custom_savedobjects.ndjson')

```

Though, that path is not valid once I build the plugin. Any ideas about a possible path?

Thank you!

**EDIT:**  
Guess I sorted out the path problem, using the `${__dirname}/${filepath}` sintax when reading the file. It works in both dev env and testing env (built plugin).

---

<div class="post-metadata">

**Author:** ![Mikhail\_Shustov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/mikhail_shustov/32/49186_2.png) [@Mikhail\_Shustov](https://discuss.elastic.co/u/Mikhail_Shustov)\
**Post date:** [January 10, 2022, 2:12pm UTC](https://discuss.elastic.co/t/7-15-kibana-plugin-development-import-saved-objects-to-specific-space/293869/3 "2022-01-10T14:12:16Z")

</div>

> [@Fabio-sama](#):
>
> throwErrorIfNamespaceSpecified

I guess `Spaces` plugin tries to prevent you from accessing internal API. Have you tried to use this experimental API created for this purpose [Copy saved objects to space API | Kibana Guide [7.16] | Elastic](https://www.elastic.co/guide/en/kibana/7.16/spaces-api-copy-saved-objects.html)?

---

<div class="post-metadata">

**Author:** ![jportner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/jportner/32/75692_2.png) [@jportner](https://discuss.elastic.co/u/jportner)\
**Post date:** [January 10, 2022, 2:37pm UTC](https://discuss.elastic.co/t/7-15-kibana-plugin-development-import-saved-objects-to-specific-space/293869/4 "2022-01-10T14:37:36Z")

</div>

> [@Fabio-sama](#):
>
> that is adding the excludeWrappers option in the getClient method. That way the wrapper that raises an error if the namespace is specified is ignored and I can then proceeed with the import with a specific space as follows:
> 
> ```auto
> importer.import({
> overwrite: true,
> createNewCopies: false,
> namespace: 'my_custom_space',
> readStream: this.createReadableStreamFromArray(epi_array)
> })
> 
> ```
> 
> It seems to work flawlessy. Is it the correct way to achieve this result?

Yes, that's the correct way to do it, and that's what we do for the Copy-to-Spaces feature:

- [https://github.com/elastic/kibana/blob/b2d36b863bc945479a12022a7a2cc4902ad2bd45/x-pack/plugins/spaces/server/lib/copy\_to\_spaces/lib/saved\_objects\_client\_opts.ts#L10-L12](https://github.com/elastic/kibana/blob/b2d36b863bc945479a12022a7a2cc4902ad2bd45/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/saved_objects_client_opts.ts#L10-L12)
- [https://github.com/elastic/kibana/blob/b2d36b863bc945479a12022a7a2cc4902ad2bd45/x-pack/plugins/spaces/server/lib/copy\_to\_spaces/copy\_to\_spaces.ts#L27](https://github.com/elastic/kibana/blob/b2d36b863bc945479a12022a7a2cc4902ad2bd45/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts#L27)
- [https://github.com/elastic/kibana/blob/b2d36b863bc945479a12022a7a2cc4902ad2bd45/x-pack/plugins/spaces/server/lib/copy\_to\_spaces/copy\_to\_spaces.ts#L29](https://github.com/elastic/kibana/blob/b2d36b863bc945479a12022a7a2cc4902ad2bd45/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts#L29)
- [https://github.com/elastic/kibana/blob/b2d36b863bc945479a12022a7a2cc4902ad2bd45/x-pack/plugins/spaces/server/lib/copy\_to\_spaces/copy\_to\_spaces.ts#L50-L55](https://github.com/elastic/kibana/blob/b2d36b863bc945479a12022a7a2cc4902ad2bd45/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts#L50-L55)

> [@Mikhail\_Shustov](#):
>
> I guess `Spaces` plugin tries to prevent you from accessing internal API. Have you tried to use this experimental API created for this purpose [Copy saved objects to space API | Kibana Guide [7.16] | Elastic](https://www.elastic.co/guide/en/kibana/7.16/spaces-api-copy-saved-objects.html)?

Yeah, the Spaces plugin doesn't want you to try to set the `namespace` when the Spaces SOC wrapper already does that based on the request path. But that's why we have the ability to exclude SOC wrappers, for edge cases like this!

---

<div class="post-metadata">

**Author:** ![Fabio-sama](https://avatars.discourse-cdn.com/v4/letter/f/b9e5f3/32.png) [@Fabio-sama](https://discuss.elastic.co/u/Fabio-sama)\
**Post date:** [January 10, 2022, 3:18pm UTC](https://discuss.elastic.co/t/7-15-kibana-plugin-development-import-saved-objects-to-specific-space/293869/5 "2022-01-10T15:18:17Z")

</div>

First of all thank you both for your replies.

> [@Mikhail\_Shustov](#):
>
> I guess `Spaces` plugin tries to prevent you from accessing internal API. Have you tried to use this experimental API created for this purpose [Copy saved objects to space API | Kibana Guide [7.16] | Elastic](https://www.elastic.co/guide/en/kibana/7.16/spaces-api-copy-saved-objects.html)?

That's the API I took inspiration from, but I do not want to merely call the API (e.g. axios), I'd rather go with a cleaner approach using something offered by Kibana developers.

> [@jportner](#):
>
> Yes, that's the correct way to do it, and that's what we do for the Copy-to-Spaces feature:

Thank you so much for your confirmation.

As for now, I changed my code to avoid the re-definition of your **createReadableStreamFromArray** method and the manual creation of a json array from a ndjson file.  
This way I avoided the installation of further dependencies (i.e. fs-ndjson and ndjson) and I simply went with something like the following

```auto
private async initCUSTOMSpace(context: RequestHandlerContext, request: KibanaRequest) {
    let namespacesResponse = await this.externalPluginsService.getExternalPlugins().spaces?.spacesService?.createSpacesClient(request).getAll();
    const namespaces = namespacesResponse?.map((namespace: GetSpaceResult) => namespace.id) as string[];
    if (!namespaces.includes(INIT_PARAMS.INIT_CUSTOM_NAMESPACE_ID)) await this.externalPluginsService.getExternalPlugins().spaces?.spacesService?.createSpacesClient(request).create(INIT_PARAMS.INIT_CUSTOM_SPACE);
    
    const custom_savedobjects_stream = fs.createReadStream(`${__dirname}/${INIT_PARAMS.INIT_CUSTOM_SO_FILEPATH}`);
    const importer = context.core.savedObjects.getImporter(context.core.savedObjects.getClient({excludedWrappers: ['spaces']}));
    importer.import({
        overwrite: true,
        createNewCopies: false,
        namespace: INIT_PARAMS.INIT_CUSTOM_NAMESPACE_ID,
        readStream: await createSavedObjectsStreamFromNdJson(custom_savedobjects_stream)
    })
}

```

Guess it's the cleanest approach I could use to achieve what I wanted and it seems to work perfectly.

Thank you

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [February 7, 2022, 3:18pm UTC](https://discuss.elastic.co/t/7-15-kibana-plugin-development-import-saved-objects-to-specific-space/293869/6 "2022-02-07T15:18:26Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
