appwrite-utils-cli 0.10.1 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -147,6 +147,7 @@ This updated CLI ensures that developers have robust tools at their fingertips t
147
147
 
148
148
  ## Changelog
149
149
 
150
+ - 0.10.02: Updated `wipeCollection` to handle errors gracefully
150
151
  - 0.10.01: Fixed `predeployCommands` to work
151
152
  - 0.10.001: Updated `deployFunction` to not updateConfig if it's already present
152
153
  - 0.10.0: Fixed `synchronize configurations` for functions, now you do not need to deploy the function first
@@ -87,19 +87,40 @@ export const fetchAndCacheCollectionByName = async (db, dbId, collectionName) =>
87
87
  }
88
88
  };
89
89
  async function wipeDocumentsFromCollection(database, databaseId, collectionId) {
90
- const initialDocuments = await database.listDocuments(databaseId, collectionId, [Query.limit(1000)]);
91
- let documents = initialDocuments.documents;
92
- while (documents.length === 1000) {
93
- const docsResponse = await database.listDocuments(databaseId, collectionId, [Query.limit(1000)]);
94
- documents = documents.concat(docsResponse.documents);
90
+ try {
91
+ const initialDocuments = await database.listDocuments(databaseId, collectionId, [Query.limit(1000)]);
92
+ let documents = initialDocuments.documents;
93
+ let totalDocuments = documents.length;
94
+ while (documents.length === 1000) {
95
+ const docsResponse = await database.listDocuments(databaseId, collectionId, [Query.limit(1000)]);
96
+ documents = documents.concat(docsResponse.documents);
97
+ totalDocuments = documents.length;
98
+ }
99
+ console.log(`Found ${totalDocuments} documents to delete`);
100
+ const maxStackSize = 25; // Reduced batch size
101
+ for (let i = 0; i < documents.length; i += maxStackSize) {
102
+ const batch = documents.slice(i, i + maxStackSize);
103
+ const deletePromises = batch.map(async (doc) => {
104
+ try {
105
+ await database.deleteDocument(databaseId, collectionId, doc.$id);
106
+ }
107
+ catch (error) {
108
+ // Skip if document doesn't exist or other non-critical errors
109
+ if (!error.message?.includes("Document with the requested ID could not be found")) {
110
+ console.error(`Failed to delete document ${doc.$id}:`, error.message);
111
+ }
112
+ }
113
+ });
114
+ await Promise.all(deletePromises);
115
+ await delay(250); // Increased delay between batches
116
+ console.log(`Deleted batch of ${batch.length} documents (${i + batch.length}/${totalDocuments})`);
117
+ }
118
+ console.log(`Completed deletion of ${totalDocuments} documents from collection ${collectionId}`);
95
119
  }
96
- const batchDeletePromises = documents.map((doc) => database.deleteDocument(databaseId, collectionId, doc.$id));
97
- const maxStackSize = 100;
98
- for (let i = 0; i < batchDeletePromises.length; i += maxStackSize) {
99
- await Promise.all(batchDeletePromises.slice(i, i + maxStackSize));
100
- await delay(100);
120
+ catch (error) {
121
+ console.error(`Error wiping documents from collection ${collectionId}:`, error);
122
+ throw error;
101
123
  }
102
- console.log(`Deleted ${documents.length} documents from collection ${collectionId}`);
103
124
  }
104
125
  export const wipeDatabase = async (database, databaseId) => {
105
126
  console.log(`Wiping database: ${databaseId}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "appwrite-utils-cli",
3
3
  "description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
4
- "version": "0.10.01",
4
+ "version": "0.10.02",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -129,31 +129,68 @@ async function wipeDocumentsFromCollection(
129
129
  databaseId: string,
130
130
  collectionId: string
131
131
  ) {
132
- const initialDocuments = await database.listDocuments(
133
- databaseId,
134
- collectionId,
135
- [Query.limit(1000)]
136
- );
137
- let documents = initialDocuments.documents;
138
- while (documents.length === 1000) {
139
- const docsResponse = await database.listDocuments(
132
+ try {
133
+ const initialDocuments = await database.listDocuments(
140
134
  databaseId,
141
135
  collectionId,
142
136
  [Query.limit(1000)]
143
137
  );
144
- documents = documents.concat(docsResponse.documents);
145
- }
146
- const batchDeletePromises = documents.map((doc) =>
147
- database.deleteDocument(databaseId, collectionId, doc.$id)
148
- );
149
- const maxStackSize = 100;
150
- for (let i = 0; i < batchDeletePromises.length; i += maxStackSize) {
151
- await Promise.all(batchDeletePromises.slice(i, i + maxStackSize));
152
- await delay(100);
138
+ let documents = initialDocuments.documents;
139
+ let totalDocuments = documents.length;
140
+
141
+ while (documents.length === 1000) {
142
+ const docsResponse = await database.listDocuments(
143
+ databaseId,
144
+ collectionId,
145
+ [Query.limit(1000)]
146
+ );
147
+ documents = documents.concat(docsResponse.documents);
148
+ totalDocuments = documents.length;
149
+ }
150
+
151
+ console.log(`Found ${totalDocuments} documents to delete`);
152
+
153
+ const maxStackSize = 25; // Reduced batch size
154
+ for (let i = 0; i < documents.length; i += maxStackSize) {
155
+ const batch = documents.slice(i, i + maxStackSize);
156
+ const deletePromises = batch.map(async (doc) => {
157
+ try {
158
+ await database.deleteDocument(databaseId, collectionId, doc.$id);
159
+ } catch (error: any) {
160
+ // Skip if document doesn't exist or other non-critical errors
161
+ if (
162
+ !error.message?.includes(
163
+ "Document with the requested ID could not be found"
164
+ )
165
+ ) {
166
+ console.error(
167
+ `Failed to delete document ${doc.$id}:`,
168
+ error.message
169
+ );
170
+ }
171
+ }
172
+ });
173
+
174
+ await Promise.all(deletePromises);
175
+ await delay(250); // Increased delay between batches
176
+
177
+ console.log(
178
+ `Deleted batch of ${batch.length} documents (${
179
+ i + batch.length
180
+ }/${totalDocuments})`
181
+ );
182
+ }
183
+
184
+ console.log(
185
+ `Completed deletion of ${totalDocuments} documents from collection ${collectionId}`
186
+ );
187
+ } catch (error) {
188
+ console.error(
189
+ `Error wiping documents from collection ${collectionId}:`,
190
+ error
191
+ );
192
+ throw error;
153
193
  }
154
- console.log(
155
- `Deleted ${documents.length} documents from collection ${collectionId}`
156
- );
157
194
  }
158
195
 
159
196
  export const wipeDatabase = async (
@@ -1,21 +0,0 @@
1
- import { AppwriteRequest, AppwriteResponse } from "appwrite-utils";
2
- /**
3
- * Main function to handle document counting requests.
4
- * @param {Object} params - The function parameters.
5
- * @param {Object} params.req - The request object.
6
- * @param {Object} params.res - The response object.
7
- * @param {Function} params.log - Logging function.
8
- * @param {Function} params.error - Error logging function.
9
- * @returns {Promise<Object>} JSON response with count or error message.
10
- */
11
- declare const _default: ({ req, res, log, error }: {
12
- req: AppwriteRequest;
13
- res: AppwriteResponse;
14
- log: (message: string) => void;
15
- error: (message: string) => void;
16
- }) => Promise<{
17
- body: Uint8Array;
18
- statusCode: number;
19
- headers: Record<string, string>;
20
- }>;
21
- export default _default;
@@ -1,114 +0,0 @@
1
- import { Client, Databases, Query } from "node-appwrite";
2
- import { AppwriteRequest, AppwriteResponse } from "appwrite-utils";
3
- import { requestSchema } from "./request.js";
4
- /**
5
- * Main function to handle document counting requests.
6
- * @param {Object} params - The function parameters.
7
- * @param {Object} params.req - The request object.
8
- * @param {Object} params.res - The response object.
9
- * @param {Function} params.log - Logging function.
10
- * @param {Function} params.error - Error logging function.
11
- * @returns {Promise<Object>} JSON response with count or error message.
12
- */
13
- export default async ({ req, res, log, error }) => {
14
- // Initialize Appwrite client
15
- const client = new Client()
16
- .setEndpoint(process.env["APPWRITE_FUNCTION_ENDPOINT"])
17
- .setProject(process.env["APPWRITE_FUNCTION_PROJECT_ID"])
18
- .setKey(req.headers["x-appwrite-key"] || "");
19
- const databases = new Databases(client);
20
- try {
21
- if (req.method === "POST") {
22
- // Parse request body
23
- const body = requestSchema.safeParse(typeof req.body === "string" ? JSON.parse(req.body) : req.body);
24
- if (!body.success) {
25
- return res.json({ success: false, error: body.error }, 400);
26
- }
27
- const { databaseId, collectionId, queries = [] } = body.data;
28
- log(`Queries: ${JSON.stringify(queries)}`);
29
- // Count documents in the specified collection
30
- const count = await countAllDocuments(log, databases, databaseId, collectionId, queries);
31
- // Return successful response with document count
32
- return res.json({
33
- success: true,
34
- count: count,
35
- });
36
- }
37
- else {
38
- // Return error for non-POST requests
39
- return res.json({ success: false, error: "Method not allowed" }, 405);
40
- }
41
- }
42
- catch (err) {
43
- // Log and return any errors
44
- error(`Error processing request: ${err}`);
45
- return res.json({ success: false, error: err.message }, 500);
46
- }
47
- };
48
- /**
49
- * Counts all documents in a collection, handling large collections efficiently.
50
- * @param {Function} log - Logging function.
51
- * @param {Databases} databases - Appwrite Databases instance.
52
- * @param {string} databaseId - ID of the database.
53
- * @param {string} collectionId - ID of the collection.
54
- * @param {string[]} queries - Array of query strings to filter documents.
55
- * @param {number} batchSize - Size of batches for processing (default: 1000).
56
- * @returns {Promise<number>} Total count of documents.
57
- */
58
- async function countAllDocuments(log, databases, databaseId, collectionId, queries = [], batchSize = 1000) {
59
- // Filter out limit and offset queries
60
- const initialQueries = queries.filter((q) => !(q.includes("limit") || q.includes("offset")));
61
- // Initial query to check if total is less than 5000
62
- const initialResponse = await databases.listDocuments(databaseId, collectionId, [...initialQueries, Query.limit(1)]);
63
- if (initialResponse.total < 5000) {
64
- log(`Total documents (from initial response): ${initialResponse.total}`);
65
- return initialResponse.total;
66
- }
67
- // If total is 5000 or more, we need to count manually
68
- let bound = 5000;
69
- // Exponential search to find an upper bound
70
- while (true) {
71
- log(`Querying for offset ${bound}`);
72
- try {
73
- const response = await databases.listDocuments(databaseId, collectionId, [
74
- ...initialQueries,
75
- Query.limit(1),
76
- Query.offset(bound),
77
- ]);
78
- if (response.documents.length === 0) {
79
- break;
80
- }
81
- bound *= 2;
82
- }
83
- catch (error) {
84
- break;
85
- }
86
- }
87
- // Binary search to find the exact count
88
- let low = Math.floor(bound / 2);
89
- let high = bound;
90
- let lastValidCount = low;
91
- while (low <= high) {
92
- const mid = Math.floor((low + high) / 2);
93
- log(`Binary search: Querying for offset ${mid}`);
94
- try {
95
- const response = await databases.listDocuments(databaseId, collectionId, [
96
- ...initialQueries,
97
- Query.limit(1),
98
- Query.offset(mid),
99
- ]);
100
- if (response.documents.length > 0) {
101
- lastValidCount = mid + 1; // +1 because offset is 0-based
102
- low = mid + 1;
103
- }
104
- else {
105
- high = mid - 1;
106
- }
107
- }
108
- catch (error) {
109
- high = mid - 1;
110
- }
111
- }
112
- log(`Total documents: ${lastValidCount}`);
113
- return lastValidCount;
114
- }
@@ -1,15 +0,0 @@
1
- import { z } from "zod";
2
- export declare const requestSchema: z.ZodObject<{
3
- databaseId: z.ZodString;
4
- collectionId: z.ZodString;
5
- queries: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
6
- }, "strip", z.ZodTypeAny, {
7
- collectionId: string;
8
- databaseId: string;
9
- queries?: string[] | undefined;
10
- }, {
11
- collectionId: string;
12
- databaseId: string;
13
- queries?: string[] | undefined;
14
- }>;
15
- export type Request = z.infer<typeof requestSchema>;
@@ -1,6 +0,0 @@
1
- import { z } from "zod";
2
- export const requestSchema = z.object({
3
- databaseId: z.string(),
4
- collectionId: z.string(),
5
- queries: z.array(z.string()).optional(),
6
- });
@@ -1,11 +0,0 @@
1
- import { AppwriteRequest, AppwriteResponse } from "appwrite-utils";
2
- export default function ({ req, res, log, error, }: {
3
- req: AppwriteRequest;
4
- res: AppwriteResponse;
5
- log: (message: string) => void;
6
- error: (message: string) => void;
7
- }): Promise<{
8
- body: Uint8Array;
9
- statusCode: number;
10
- headers: Record<string, string>;
11
- }>;
@@ -1,11 +0,0 @@
1
- import { Client } from "node-appwrite";
2
- import { AppwriteRequest, AppwriteResponse } from "appwrite-utils";
3
- export default async function ({ req, res, log, error, }) {
4
- const client = new Client()
5
- .setEndpoint(process.env["APPWRITE_FUNCTION_ENDPOINT"])
6
- .setProject(process.env["APPWRITE_FUNCTION_PROJECT_ID"])
7
- .setKey(req.headers["x-appwrite-key"] || "");
8
- return res.json({
9
- message: "Hello from TypeScript function!",
10
- });
11
- }