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

Split integrations.ts into handlers & data #40685

Merged

Conversation

jfsiii
Copy link
Contributor

@jfsiii jfsiii commented Jul 9, 2019

Summary

200+ line server/integrations.ts split into {data,handlers}.ts around 100 lines each. The logic/behavior is the same. Functions were just moved around and I think things are much more clear.

handlers functions parse the requests and call the functions from data.

data functions coordinate with the registry, saved objects, etc.

John Schulz added 4 commits July 9, 2019 11:15
@jfsiii jfsiii requested a review from jasonrhodes July 9, 2019 20:19
@jfsiii jfsiii changed the title Split up integrations Split integrations.ts into handlers & data Jul 9, 2019
@@ -49,15 +49,17 @@ export interface RegistryPackage {
// the public HTTP response types
export type IntegrationList = IntegrationListItem[];

export type IntegrationListItem = Installed<RegistryListItem> | NotInstalled<RegistryListItem>;
export type IntegrationListItem = Installable<RegistryListItem>;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

So you don’t have to do the | each time. Went with Installable over Maybe*, Either*, etc.

import { SAVED_OBJECT_TYPE } from '../../common/constants';
import * as Registry from '../registry';

export async function getIntegrations(client: SavedObjectsClientContract) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These functions all accept a saved object client. Mainly because we get the client from the request, but it also means we can inject a client. Figure we’ll come back to this over time and when the client lands in core .

Copy link
Member

Choose a reason for hiding this comment

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

So happy to have TS here...

}
}

function createInstallableFrom<T>(from: T, savedObject?: InstallationSavedObject): Installable<T> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Dropped all the overloads

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

This is consistent with other registry functions. Returned to nested for loops internally.
@@ -41,7 +45,10 @@ export async function handleRequestInstall(req: InstallAssetRequest) {

if (asset === 'dashboard') {
const client = getClient(req);
const object = await installAssets(client, pkgkey, asset);
const object = await installAssets(client, pkgkey, (entry: ArchiveEntry) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Use a filter function like other registry functions

@@ -28,9 +30,13 @@ export async function getArchiveInfo(
const paths: string[] = [];
const onEntry = (entry: ArchiveEntry) => {
const { path, buffer } = entry;
paths.push(path);
const { file } = pathParts(path);
if (!file) return;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Don’t add directories to the paths. Only files.

collectReferences(map, { path, desiredType });
return map;
}, new Map());
export async function getObjects(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Condensed getObjects & collectReferences into one function. Added a ton of comments.

Went with nested fors vs several map, reduce calls and flatten. For context, these might be the first for loops I’ve written in 5 years. I’m not overly concerned with performance but would like to skip the garbage creation/collection of traversing & transforming as much as possible. I also find this the most clear way to express what we’re doing.

Copy link
Member

Choose a reason for hiding this comment

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

Love these comments. Totally fine with the for loops, I don't love mutating objects that get passed to other functions, so having this all in one function really helps follow that mutation thread for me.

Copy link
Member

Choose a reason for hiding this comment

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

This feels like a function I'd love to have tests for. Can we log a ticket to do that soon, if not now? Not concerned about 100% coverage or anything but testing a few of these functions would be a great start.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For sure. Created this very minimal issue but feel free to add any others you think of.

if (!asset.id) asset.id = file.replace('.json', '');

return ensureJsonValues(asset);
}
Copy link
Member

Choose a reason for hiding this comment

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

Mind adding a few more comments on these two functions to explain the diff between getAsset and getObject? I think I follow but a little lost on what's coming out of the cache that needs to be toString-ified.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 5437feb

}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface DeleteAssetRequest extends InstallAssetRequest {}
Copy link
Member

Choose a reason for hiding this comment

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

Are we expecting these to diverge at some point?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if I follow, but I made some changes here. Can you look at 13cef20 and let me know if you still have questions/suggestions?

}));

const results = await client.bulkGet(searchObjects);
const savedObjects: InstallationSavedObject[] = results.saved_objects.filter(o => !o.error); // ignore errors for now
Copy link
Member

@jasonrhodes jasonrhodes Jul 10, 2019

Choose a reason for hiding this comment

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

do you know if the saved object client takes a type generic for what its methods return? I like being able to do:

const results = await client.bulkGet<InstallationSavedObject>(searchObjects);

and not having to explicitly type the return of the filter on the next line, if the client method handles that properly. Not a blocker just curious.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I haven’t seen that before but, yeah, they do.

Or something quite similar

async bulkGet<T extends SavedObjectAttributes = any>(
&
export type SavedObjectAttribute =
| string
| number
| boolean
| null
| undefined
| SavedObjectAttributes
| SavedObjectAttributes[];
/**
*
* @public
*/
export interface SavedObjectAttributes {
[key: string]: SavedObjectAttribute | SavedObjectAttribute[];
}

Take a look at 98dfac8. I updated all the client.* calls and removed the explicit typing of their return values.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

return integrationList;
}

export async function getIntegrationInfo(client: SavedObjectsClientContract, pkgkey: string) {
Copy link
Member

Choose a reason for hiding this comment

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

Is this the single resource version of getIntegrations?

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, IIUC. It’s what’s used for the detail page. getIntegrations is what’s used for for the list view.

}

export async function getInstallationObject(
client: SavedObjectsClientContract,
Copy link
Member

Choose a reason for hiding this comment

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

And this is "get the current install state for this one integration"?

@@ -0,0 +1,109 @@
/*
Copy link
Member

Choose a reason for hiding this comment

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

I think a few simple comments on top of each of the exported functions in this file to explain what they do in relationship to each other would go a long way in helping new folks understand the flow here, and then we can massage the actual names of the functions as we better understand how it all fits together or what we need for other cases, etc. Otherwise this is all really clear and clean.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. Breaking this up helped me see some patterns.

If it’s OK with you, I’d like to hold off on that until I add another asset type (e.g. #38609) because I think that might influence things. Things don’t need to be “final” before I add the comments, just looking to get back to making after this refactoring then see how things fit together.

Copy link
Member

@jasonrhodes jasonrhodes left a comment

Choose a reason for hiding this comment

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

Looks good, had a few suggestions for clarifying via a few more comments, and I had a couple questions, but it's mergeable as-is.

John Schulz added 4 commits July 10, 2019 16:18
Don't need a generic accessor (e.g. for docs, images, etc) yet. Will factor out when it's needed.
I think this is clearer. It also makes sure we don't loop over any keys other than the "root" ones we just matched (vs all that are in the map/accumulator).
@jfsiii jfsiii mentioned this pull request Jul 10, 2019
2 tasks
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@jfsiii jfsiii merged commit 15b54d5 into elastic:feature-integrations-manager Jul 10, 2019
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@jfsiii jfsiii deleted the split-up-integrations branch December 30, 2019 16:12
@jen-huang jen-huang added the Team:Fleet Team label for Observability Data Collection Fleet team label Mar 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Team:Fleet Team label for Observability Data Collection Fleet team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants