From aac18a6eaece8f4d4b488e4dc6db0c2cf90e619d Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 18 Jun 2020 15:41:56 +0100 Subject: [PATCH] improved documentation --- .../plugins/encrypted_saved_objects/README.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/x-pack/plugins/encrypted_saved_objects/README.md b/x-pack/plugins/encrypted_saved_objects/README.md index 2f0af9e866797..887ba85976cb2 100644 --- a/x-pack/plugins/encrypted_saved_objects/README.md +++ b/x-pack/plugins/encrypted_saved_objects/README.md @@ -99,6 +99,71 @@ const savedObjectWithDecryptedContent = await esoClient.getDecryptedAsInternalU one would pass to `SavedObjectsClient.get`. These argument allows to specify `namespace` property that, for example, is required if Saved Object was created within a non-default space. +### defining migrations +EncryptedSavedObjects rely on standard SavedObject migrations, but due to the additional complexity introduced by the need to decrypt and reencrypt the migrated document, there are some caveats to how we support this. +The good news is, most of this complexity is abstracted away by the plugin and all you need to do is leverage our api. + +The `EncryptedSavedObjects` Plugin _SetupContract_ exposes an `createMigration` api which facilitates defining a migration for your EncryptedSavedObject type. + +The `createMigration` function takes four arguments: + +|Argument|Description|Type| +|---|---|---| +|isMigrationNeededPredicate|A predicate which is called for each document, prior to being decrypted, which confirms whether a document requires migration or not. This predicate is important as the decryption step is costly and we would rather not decrypt and re-encrypt a document if we can avoid it.|function| +|migration|A migration function which will migrate each decrypted document from the old shape to the new one.|function| +|inputType|An `EncryptedSavedObjectTypeRegistration` which describes the ESOType of the input (the document prior to migration).|object| +|migratedType| Optional. An `EncryptedSavedObjectTypeRegistration` which describes the ESOType of the output (the document after migration). If this type isn't provided, we'll assume the type remains unchanged before and after the migration.|object| + +For example: + +```typescript +const migration790 = encryptedSavedObjects.createMigration( + function shouldbeMigrated(doc): doc is SavedObjectUnsanitizedDoc { + return doc.consumer === 'alerting' || doc.consumer === undefined; + }, + (doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc => { + const { + attributes: { consumer }, + } = doc; + return { + ...doc, + attributes: { + ...doc.attributes, + consumer: consumer === 'alerting' || !consumer ? 'alerts' : consumer, + }, + }; + }, + // type hasn't changed as the field we're updating is not an encrypted one + { + type: 'alert', + attributesToEncrypt: new Set(['apiKey']), + attributesToExcludeFromAAD: new Set(['mutedInstanceIds', 'updatedBy']), + } +); +``` + +In the above example you can see thwe following: +1. In `shouldbeMigrated` we limit the migrated alerts to those whose `consumer` field equals `alerting` or is undefined. +2. In the migration function we then migrate the value of `consumer` to the value we want (`alerts` or `unknown`, dependsing on the current value). In this function we can assume that only documents with a `consumer` of `alerting` or `undefined` will be passed in, but it's still safest not to, and so we use the current `consumer` as the default when needed. +3. We provide the type, which remains unchanged across this migration and so we can omit the fourth argument. + +As we said above, an EncryptedSavedObject migration is a normal SavedObjects migration, and so we can plug it into the underlying SavedObject just like any other kind of migration: + +```typescript +savedObjects.registerType({ + name: 'alert', + hidden: true, + namespaceType: 'single', + migrations: { + // apply this migration in 7.9.0 + '7.9.0': migration790, + }, + mappings: { + //... + }, +}); +``` + ## Testing ### Unit tests