appwrite-utils
Version:
`appwrite-utils` is a comprehensive TypeScript library designed to streamline the development process for Appwrite projects. Version 1.0.0 aligns with the YAML-first architecture of `appwrite-utils-cli`, providing enhanced integration capabilities and rob
126 lines (125 loc) • 4.73 kB
JavaScript
import { AppwriteException, Query } from "appwrite";
/**
* Tries to execute the given createFunction and retries up to 5 times if it fails.
*
* @param {() => Promise<any>} createFunction - The function to be executed.
* @param {number} [attemptNum=0] - The number of attempts made so far (default: 0).
* @return {Promise<any>} - A promise that resolves to the result of the createFunction or rejects with an error if it fails after 5 attempts.
*/
export const tryAwaitWithRetry = async (createFunction, attemptNum = 0) => {
try {
return await createFunction();
}
catch (error) {
if (error instanceof AppwriteException &&
(error.message.toLowerCase().includes("fetch failed") ||
error.message.toLowerCase().includes("server error"))) {
console.log(`Fetch failed on attempt ${attemptNum}. Retrying...`);
if (attemptNum > 5) {
throw error;
}
return tryAwaitWithRetry(createFunction, attemptNum + 1);
}
throw error;
}
};
/**
* Compares existing and updated objects to determine if an update is needed.
* Filters out Appwrite system fields before comparison.
*
* @param existing - The existing object
* @param updated - The updated object
* @returns true if the object needs to be updated, false otherwise
*/
export const objectNeedsUpdate = (existing, updated) => {
if (!existing || !updated) {
return true;
}
// Create clean versions without Appwrite system fields
const existingClean = Object.fromEntries(Object.entries({
...existing,
}).filter(([key]) => !key.startsWith("$")));
delete existingClean.$id;
delete existingClean.$createdAt;
delete existingClean.$updatedAt;
delete existingClean.$permissions;
delete existingClean.$collectionId;
delete existingClean.$databaseId;
delete existingClean.databaseId;
const updatedClean = Object.fromEntries(Object.entries({
...updated,
}).filter(([key]) => !key.startsWith("$")));
delete updatedClean.$id;
delete updatedClean.$createdAt;
delete updatedClean.$updatedAt;
delete updatedClean.$permissions;
delete updatedClean.$collectionId;
delete updatedClean.$databaseId;
delete updatedClean.databaseId;
// Check for new fields or changed values
const allKeys = new Set([
...Object.keys(existingClean),
...Object.keys(updatedClean),
]);
return Array.from(allKeys).some((key) => {
const existingValue = existingClean[key];
const updatedValue = updatedClean[key];
// If a value went from null/undefined to a real value or vice versa, that's a change
const existingIsEmpty = existingValue === null || existingValue === undefined;
const updatedIsEmpty = updatedValue === null || updatedValue === undefined;
if (existingIsEmpty !== updatedIsEmpty) {
return true;
}
// If both values exist but are different, that's a change
if (!existingIsEmpty && !updatedIsEmpty && existingValue !== updatedValue) {
return true;
}
return false;
});
};
/**
* Removes Appwrite system fields from an object before create/update operations.
*
* @param obj - The object to clean
* @returns A new object with Appwrite system fields removed
*/
export const cleanObjectForAppwrite = (obj) => {
const cleaned = { ...obj };
delete cleaned.$id;
delete cleaned.$createdAt;
delete cleaned.$updatedAt;
delete cleaned.$permissions;
delete cleaned.$collectionId;
delete cleaned.$databaseId;
delete cleaned.databaseId;
return cleaned;
};
/**
* Appwrite only supports Query.equal("$id", [someArrayOfIds])
* with 100 ids at a time, so we have this helper function to list documents in batches
*
* @param databases - Appwrite Databases instance
* @param databaseId - Database ID
* @param collectionId - Collection ID
* @param field - Field to query (typically "$id")
* @param ids - Array of IDs to query
* @returns Array of documents
*/
export const listDocumentsBatched = async (databases, databaseId, collectionId, field, ids) => {
const batches = ids.reduce((acc, id, index) => {
const batchIndex = Math.floor(index / 100);
if (!acc[batchIndex]) {
acc[batchIndex] = [];
}
acc[batchIndex].push(id);
return acc;
}, []);
const documents = await Promise.all(batches.map((batch) => databases.listDocuments(databaseId, collectionId, [
Query.equal(field, batch),
])));
return documents
.flat()
.map((docList) => docList.documents)
.flat()
.map((doc) => doc);
};