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

Convert frontend-core API client to Typescript #15096

Merged
merged 142 commits into from
Dec 17, 2024
Merged

Conversation

aptkingston
Copy link
Member

@aptkingston aptkingston commented Nov 29, 2024

Description

This PR contains the full conversion of the frontend-core API client to typescript.

Along with the TS conversion, certain endpoints have had their arguments flattened and the calling code updated accordingly.

My usage of type vs interface is pretty much arbitrary so I welcome any recommendations or changes to that, as well as the location of types.

All new typescript code is contained in frontend-core/src/api. Every other change is just to accommodate function signature changes, which are a combination of flattening params and rearranging things slightly to suit types better.

Copy link

qa-wolf bot commented Nov 29, 2024

QA Wolf here! As you write new code it's important that your test coverage is keeping up.
Click here to request test coverage for this PR!

if (selectedRows?.length) {
payload.rows = selectedRows.map(row => row._id)
}
if (sorting) {
payload.search.sort = sorting.sortColumn
payload.search.sortOrder = sorting.sortOrder
payload.sort = sorting.sortColumn
Copy link
Collaborator

Choose a reason for hiding this comment

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

❤️
I guess this was broken? Or were we supporting both formats?

Copy link
Member Author

Choose a reason for hiding this comment

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

It wasn't broken, but I've just flattened the structure a little to make it simpler. We used to do this logic inside the API client code, but there was no point in wrapping things in a search object just to pull them out again later. So the result sent to the API is identical but it's a bit simpler to use it now :)

export function createPluginsStore() {
const { subscribe, set, update } = writable<Plugin[]>([])

async function load() {
const plugins = await API.getPlugins()
const plugins = (await API.getPlugins()) as Plugin[]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could the API not be typed, instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

The API actually is typed and returns a FetchPluginResponse, which is typed as

export type FetchPluginResponse = Plugin[]

so I'm only casting to Plugin[] because I'd prefer to store that type, even though there is no difference. It just feels wrong to store FetchPluginResonse in the store when it's actually just an array of plugins. I'm happy remove this if you'd prefer - I'd actually appreciate knowing what you think is better because I knew this casting was redundant but it just seemed "nicer".

Copy link
Collaborator

@mike12345567 mike12345567 Dec 16, 2024

Choose a reason for hiding this comment

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

I think my preference is to do something like const plugins: Plugin[] = await API.getPlugins() in these type of scenarios, this isn't casting in this case as Typescript will infer whether the types overlap enough for it to work.

This is similar to using a base class in OO, e.g. a Pet and an extended class Dog - you can do something like const pet: Pet = new Dog(), however in TS this is a lot more powerful since it looks into the structure of the two types rather than using inheritance.

Using as Plugin[] is more dangerous, because you are forcing the conversion - there is some protections still if the two types don't overlap at all/it makes no sense, but you'll lose protections around things like undefined checks.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good! I'll go for that approach instead of the as xxx. Thank you 🙏

"../pro/src",
"../types/src",
"../shared-core/src",
"../string-templates/src"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Good catch!

Copy link
Collaborator

@adrinr adrinr left a comment

Choose a reason for hiding this comment

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

LGTM! Good job :)
Just left some minor comments

}
} catch (error) {
delete cache[url]
return null
throw `Failed to parse response: ${error}`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you know the reason why we were not throwing, before?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it was probably just a mistake from before, but I don't honestly know... We need to throw now with TS, since we can't return the proper type when we hit this - but yea I think we probably should have been always throwing, for correctness.

suppressErrors: boolean
}

export type APIClient = BaseAPIClient &
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hectic, but good typing! :)

@aptkingston
Copy link
Member Author

Thanks for the review @adrinr! This was a monster 😅

name: detail.restoreBackupName,
})
await fetchBackups(filterOpt, page)
} else if (detail.type === "backupUpdate") {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is the backup update no longer required?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope I believe we actually stopped using and allowing custom naming of backups a while ago, so this code existed but was never used. So just pruning dead code with this.

@@ -35,7 +35,24 @@ export function createUsersStore() {
}

async function invite(payload) {
return API.inviteUsers(payload)
const users = payload.map(user => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume all this extra work is to make sure that the user fits the API typing?

Copy link
Member Author

@aptkingston aptkingston Dec 16, 2024

Choose a reason for hiding this comment

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

Yep I believe so! This code is basically just cut and pasted from inside the API client, but I'd rather expose it here and have a proper typed structure as the payload for the inviteUsers call, rather than what we used to do which is pass in an arbitrary frontend structure which then gets transformed into the proper typed structure inside the API client code before being sent.

This just moves all the frontend logic here and keeps the param clean and typed 👌

* @param tableId the ID of the table
* @param rowActionId the ID of the row action to update
*/
update: async ({ tableId, rowActionId, name }) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Was the update no longer needed?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep this was also dead code! Row actions are now renamed by renaming the automation, using the normal automation endpoint, rather than via this one.

Copy link
Collaborator

@mike12345567 mike12345567 left a comment

Choose a reason for hiding this comment

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

LGTM - massive improvement here, I can see how this will populate well to the stores!

@aptkingston aptkingston merged commit b7d7946 into master Dec 17, 2024
20 checks passed
@aptkingston aptkingston deleted the frontend-core-ts-2 branch December 17, 2024 10:22
@github-actions github-actions bot locked and limited conversation to collaborators Dec 17, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants