-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Split integrations.ts into handlers & data #40685
Conversation
Essentially a mechanical refactor for now. Will work on reducing the work done in the handlers in later commits/PRs.
Behavior should be unchanged.
@@ -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>; |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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 .
There was a problem hiding this comment.
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> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dropped all the overloads
💔 Build Failed |
💚 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) => { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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 for
s 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 {} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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>( |
kibana/src/core/server/saved_objects/service/saved_objects_client.ts
Lines 149 to 164 in 40a1bde
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.
💚 Build Succeeded |
return integrationList; | ||
} | ||
|
||
export async function getIntegrationInfo(client: SavedObjectsClientContract, pkgkey: string) { |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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 @@ | |||
/* |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
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).
💚 Build Succeeded |
💚 Build Succeeded |
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 fromdata
.data
functions coordinate with the registry, saved objects, etc.