Skip to content

Commit

Permalink
perf: πŸš€API Endpoint Optimisation (#409)
Browse files Browse the repository at this point in the history
* perf: πŸš€ Optimised API Keys store in the API

* perf: πŸš„ Optimised Invitations store in the API

* perf: πŸš€ Optimised Sessions store in the API

* perf: 🏍️ Optimised Users store in the API

* perf: πŸš€ Optimised Libraries store in the API

* perf: πŸš„ Optimised Requests store in the API

* perf: 🏍️ Optimised Tasks store in the API

* perf: πŸš€ Optimised Webhooks store in the API

* docs: πŸ“š Added comments to the endpoints
  • Loading branch information
JamsRepos authored May 5, 2024
1 parent 819223e commit 82d4cf5
Show file tree
Hide file tree
Showing 9 changed files with 356 additions and 577 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export default defineComponent({
if (await this.$modal.confirmModal(this.__("Are you sure?"), this.__("Are you sure you want to delete this webhook?"))) {
this.disabled.delete = true;
await this.deleteWebhook(this.webhook.id).finally(() => (this.disabled.delete = false));
this.$toast.info(this.__("Webhook deleted successfully"));
} else {
this.$toast.info(this.__("Webhook deletion cancelled"));
}
Expand Down
86 changes: 30 additions & 56 deletions apps/wizarr-frontend/src/stores/apikeys.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,51 @@
// Import the types for API keys and the Pinia library function for creating a store
import type { APIKey, APIKeys } from '@/types/api/apikeys';

import { defineStore } from 'pinia';

// Define the shape of the state in this store
interface APIKeyStoreState {
apikeys: APIKeys;
}

// Define and export a store named 'apikeys' using the Pinia library
export const useAPIKeyStore = defineStore('apikeys', {
// Define the initial state of the store
state: (): APIKeyStoreState => ({
apikeys: [],
}),
// Define actions that can mutate the state
actions: {
// Asynchronously fetches API keys from the server and updates the state
async getAPIKeys() {
// Get the API keys from the API
const apikeys = await this.$axios
const response = await this.$axios
.get<APIKeys, { data: APIKeys }>('/api/apikeys')
.catch(() => {
this.$toast.error('Could not get API keys');
return null;
});

// If the API keys are null, return
if (apikeys === null) return;

// Update the API keys that are already in the store
this.apikeys.forEach((apikey, index) => {
const new_apikey = apikeys.data.find(
(new_apikey: APIKey) => new_apikey.id === apikey.id,
);
if (new_apikey) this.apikeys[index] = new_apikey;
});

// Add the new API keys to the store if they don't exist
apikeys.data.forEach((apikey: APIKey) => {
if (
!this.apikeys.find(
(old_apikey) => old_apikey.id === apikey.id,
)
)
this.apikeys.push(apikey);
});

// Remove the API keys that were not in the response
this.apikeys.forEach((apikey, index) => {
if (
!apikeys.data.find(
(new_apikey: APIKey) => new_apikey.id === apikey.id,
)
)
this.apikeys.splice(index, 1);
if (response !== null) {
this.updateAPIKeys(response.data);
}
},
// Updates the current apikeys state with new data
updateAPIKeys(newAPIKeys: APIKeys) {
const newAPIKeyMap = new Map(newAPIKeys.map(key => [key.id, key]));
const updatedAPIKeys = this.apikeys.map(apikey => newAPIKeyMap.get(apikey.id) || apikey);
newAPIKeyMap.forEach((apikey, id) => {
if (!this.apikeys.some(k => k.id === id)) {
updatedAPIKeys.push(apikey);
}
});

// Return the API keys
return apikeys.data;
this.apikeys = updatedAPIKeys.filter(apikey => newAPIKeyMap.has(apikey.id));
},
// Creates a new API key on the server and updates the local state if successful
async createAPIKey(apikey: Partial<APIKey>) {
// Convert the API key to a FormData object
const formData = new FormData();

Object.keys(apikey).forEach((key) => {
// @ts-ignore
formData.append(key, apikey[key]);
});

// Create the API key
const response = await this.$axios
.post('/api/apikeys', formData, { disableErrorToast: true })
.catch((err) => {
Expand All @@ -72,33 +54,25 @@ export const useAPIKeyStore = defineStore('apikeys', {
return null;
});

// If the response is null, return
if (response === null) return;

// Add the API key to the store
this.apikeys.push(response.data as APIKey);

// Return the API key
return response.data as APIKey;
if (response !== null) {
this.apikeys.push(response.data as APIKey);
return response.data as APIKey;
}
},
// Deletes an API key from the server and removes it from the local state if successful
async deleteAPIKey(id: number) {
// Delete the API key from the API
const response = await this.$axios
.delete(`/api/apikeys/${id}`, { disableInfoToast: true })
.catch(() => {
this.$toast.error('Could not delete API key');
return null;
});

// If the response is null, return
if (response === null) return;

// Remove the API key from the store
const index = this.apikeys.findIndex(
(apikey: APIKey) => apikey.id === id,
);
if (index !== -1) this.apikeys.splice(index, 1);
if (response !== null) {
this.apikeys = this.apikeys.filter(apikey => apikey.id !== id);
}
},
},
// Persist the state of the store to local storage or another persistence layer
persist: true,
});
89 changes: 32 additions & 57 deletions apps/wizarr-frontend/src/stores/invitations.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,46 @@
// Import the types for invitations and the Pinia library function for creating a store
import type { Invitation, Invitations } from '@/types/api/invitations';

import { defineStore } from 'pinia';

// Define the shape of the state in this store
interface InvitationStoreState {
invitations: any[];
invitations: Invitations;
}

// Define and export a store named 'invitations' using the Pinia library
export const useInvitationStore = defineStore('invitations', {
// Define the initial state of the store
state: (): InvitationStoreState => ({
invitations: [] as Invitations,
invitations: [],
}),
// Define actions that can mutate the state
actions: {
// Asynchronously fetches invitations from the server and updates the state
async getInvitations() {
// Get invites from API
const invitations = await this.$axios
const response = await this.$axios
.get<Invitations, { data: Invitations }>('/api/invitations')
.catch(() => {
this.$toast.error('Could not get invitations');
return null;
});

// If the invites are null, return
if (invitations === null) return;

// Update the invites that are already in the store
this.invitations.forEach((invite, index) => {
const new_invitation = invitations.data.find(
(new_invitation: Invitation) =>
new_invitation.id === invite.id,
);
if (new_invitation) this.invitations[index] = new_invitation;
});

// Add the new invites to the store if they don't exist
invitations.data.forEach((invitation: Invitation) => {
if (
!this.invitations.find(
(old_invitation) => old_invitation.id === invitation.id,
)
)
this.invitations.push(invitation);
});

// Remove the invites that were not in the response
this.invitations.forEach((invitation, index) => {
if (
!invitations.data.find(
(new_invitation: Invitation) =>
new_invitation.id === invitation.id,
)
)
this.invitations.splice(index, 1);
if (response !== null) {
this.updateInvitations(response.data);
}
},
// Updates the current invitations state with new data
updateInvitations(newInvitations: Invitations) {
const newInvitationMap = new Map(newInvitations.map(invite => [invite.id, invite]));
const updatedInvitations = this.invitations.map(invite => newInvitationMap.get(invite.id) || invite);
newInvitationMap.forEach((invite, id) => {
if (!this.invitations.some(i => i.id === id)) {
updatedInvitations.push(invite);
}
});

// Return the invites
return invitations.data;
this.invitations = updatedInvitations.filter(invite => newInvitationMap.has(invite.id));
},
// Creates a new invitation on the server and updates the local state if successful
async createInvitation(invitation: FormData | Partial<Invitation>) {
// Create the invite
const response = await this.$axios
.post('/api/invitations', invitation, {
disableErrorToast: true,
Expand All @@ -68,17 +51,13 @@ export const useInvitationStore = defineStore('invitations', {
return null;
});

// If the response is null, return
if (response === null) return;

// Add the invite to the store
this.invitations.push(response.data as Invitation);

// Return the invite
return response.data as Invitation;
if (response !== null) {
this.invitations.push(response.data as Invitation);
return response.data as Invitation;
}
},
// Deletes an invitation from the server and removes it from the local state if successful
async deleteInvitation(id: number) {
// Delete the invite from the API
const response = await this.$axios
.delete(`/api/invitations/${id}`, { disableInfoToast: true })
.catch((err) => {
Expand All @@ -87,15 +66,11 @@ export const useInvitationStore = defineStore('invitations', {
return null;
});

// If the response is null, return
if (response === null) return;

// Remove the invite from the store
const index = this.invitations.findIndex(
(invitation: Invitation) => invitation.id === id,
);
if (index !== -1) this.invitations.splice(index, 1);
if (response !== null) {
this.invitations = this.invitations.filter(invitation => invitation.id !== id);
}
},
},
// Persist the state of the store to local storage or another persistence layer
persist: true,
});
Loading

0 comments on commit 82d4cf5

Please sign in to comment.