Appwrite-Abfragen: select, notEqual, countDocuments und Nachbau dazu
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8c1055e8a0
commit
2dd9c0dec7
3 files changed
+190
-7
No files matched your search
@@ -11,9 +11,12 @@ import http from 'node:http'
|
||||
*
|
||||
* Bulk-Create (POST .../documents mit { documents: [{ $id, ...felder }] }, 1 bis 100 Dokumente) ist alles oder
|
||||
* nichts: vorhandene oder doppelte $id => 409, ungueltige $id, unbekanntes Attribut oder ungueltiges Datum => 400,
|
||||
* dann ist nichts geschrieben. Abfragen: equal, greaterThan(Equal), lessThan(Equal), between (inklusiv), orderAsc/
|
||||
* orderDesc, limit, cursorAfter. Zwei Werte, die beide wie "YYYY-MM-DDT..." aussehen, vergleicht er als Zeitpunkte
|
||||
* (Schreibweise Z, +00:00, +02:00 ist egal), alle anderen wie bisher.
|
||||
* dann ist nichts geschrieben. Abfragen: equal, notEqual (mit Liste: keiner von; ein Dokument ohne das Feld passt),
|
||||
* greaterThan(Equal), lessThan(Equal), between (inklusiv), orderAsc/orderDesc, limit, cursorAfter, select (nur die
|
||||
* genannten Felder plus alle $-Felder). Die Antwort traegt total = Treffer der Filter, unabhaengig von limit und
|
||||
* cursorAfter. listCalls(collection?) / resetListCalls() zeichnen die Listenabfragen auf. Zwei Werte, die beide
|
||||
* wie "YYYY-MM-DDT..." aussehen, vergleicht er als Zeitpunkte (Schreibweise Z, +00:00, +02:00 ist egal), alle
|
||||
* anderen wie bisher.
|
||||
*
|
||||
* Optionen (ohne sie verhaelt sich der Nachbau wie zuvor):
|
||||
* datetimes { collection: ['feld', ...] } Datumsfelder; beim Schreiben (Anlegen, Aendern, Bulk, seed) wie
|
||||
@@ -33,6 +36,8 @@ export async function startAppwriteMock({ schema = {}, datetimes = {}, defaultLi
|
||||
let lastLocale = null
|
||||
let failUserLookups = false
|
||||
let sessionDeletes = 0
|
||||
// Aufzeichnung der Listenabfragen (GET .../documents ohne ID): [{ collection, queries: [geparste Abfragen] }]
|
||||
let listLog = []
|
||||
|
||||
// Wie Appwrite (Validator UID): 'unique()' ist erlaubt, sonst [A-Za-z0-9][A-Za-z0-9._-]{0,35}
|
||||
const validDocumentId = (id) => id === 'unique()' || (typeof id === 'string' && /^[A-Za-z0-9][A-Za-z0-9._-]{0,35}$/.test(id))
|
||||
@@ -127,6 +132,8 @@ export async function startAppwriteMock({ schema = {}, datetimes = {}, defaultLi
|
||||
switch (query.method) {
|
||||
case 'equal':
|
||||
return query.values.some((candidate) => same(value, candidate))
|
||||
case 'notEqual':
|
||||
return !query.values.some((candidate) => same(value, candidate))
|
||||
case 'greaterThan':
|
||||
return greater(value, first)
|
||||
case 'greaterThanEqual':
|
||||
@@ -142,7 +149,10 @@ export async function startAppwriteMock({ schema = {}, datetimes = {}, defaultLi
|
||||
}
|
||||
}
|
||||
|
||||
const supportedQueries = ['equal', 'greaterThan', 'greaterThanEqual', 'lessThan', 'lessThanEqual', 'between', 'orderAsc', 'orderDesc', 'limit', 'cursorAfter']
|
||||
const supportedQueries = ['equal', 'greaterThan', 'greaterThanEqual', 'lessThan', 'lessThanEqual', 'between', 'notEqual', 'orderAsc', 'orderDesc', 'limit', 'cursorAfter', 'select']
|
||||
|
||||
// Nur die genannten Felder plus alle $-Felder (wie Appwrite); $seq ist intern und faellt in publicDoc weg
|
||||
const project = (doc, attributes) => Object.fromEntries(Object.entries(doc).filter(([key]) => key.startsWith('$') || attributes.includes(key)))
|
||||
|
||||
const list = (collection, rawQueries) => {
|
||||
const queries = rawQueries.map((q) => JSON.parse(q))
|
||||
@@ -153,7 +163,11 @@ export async function startAppwriteMock({ schema = {}, datetimes = {}, defaultLi
|
||||
if (cursorId !== undefined && !docs(collection).has(cursorId)) {
|
||||
return [400, { message: 'The cursor is invalid. This can happen if the item represented by the cursor has been deleted.', code: 400, type: 'general_cursor_not_found' }]
|
||||
}
|
||||
let result = [...docs(collection).values()].filter((doc) => doc.$id === cursorId || queries.every((q) => matches(doc, q)))
|
||||
const all = [...docs(collection).values()]
|
||||
const hit = (doc) => queries.every((q) => matches(doc, q))
|
||||
// total wie Appwrite: alle Treffer der Filter, ohne limit und cursorAfter
|
||||
const total = all.filter(hit).length
|
||||
let result = all.filter((doc) => doc.$id === cursorId || hit(doc))
|
||||
for (const q of queries.filter((q) => q.method === 'orderAsc' || q.method === 'orderDesc')) {
|
||||
const dir = q.method === 'orderAsc' ? 1 : -1
|
||||
result.sort((a, b) => {
|
||||
@@ -165,7 +179,9 @@ export async function startAppwriteMock({ schema = {}, datetimes = {}, defaultLi
|
||||
if (cursorId !== undefined) result = result.slice(result.findIndex((doc) => doc.$id === cursorId) + 1)
|
||||
const limit = queries.find((q) => q.method === 'limit')?.values[0] ?? defaultLimit
|
||||
if (limit !== null) result = result.slice(0, limit)
|
||||
return [200, { total: result.length, documents: result.map(publicDoc) }]
|
||||
const selected = queries.filter((q) => q.method === 'select').flatMap((q) => q.values)
|
||||
if (queries.some((q) => q.method === 'select')) result = result.map((doc) => project(doc, selected))
|
||||
return [200, { total, documents: result.map(publicDoc) }]
|
||||
}
|
||||
|
||||
const publicDoc = ({ $seq, ...doc }) => doc
|
||||
@@ -324,6 +340,8 @@ export async function startAppwriteMock({ schema = {}, datetimes = {}, defaultLi
|
||||
const store = docs(collection)
|
||||
const notFound = [404, { message: 'Document with the requested ID could not be found.', code: 404, type: 'document_not_found' }]
|
||||
|
||||
if (!id && req.method === 'GET') listLog.push({ collection, queries: url.searchParams.getAll('queries[]').map((q) => JSON.parse(q)) })
|
||||
|
||||
// Eingeplanter Fehler (failNext): genau eine passende Anfrage scheitert
|
||||
if (
|
||||
pendingFailure &&
|
||||
@@ -389,6 +407,11 @@ export async function startAppwriteMock({ schema = {}, datetimes = {}, defaultLi
|
||||
url: `http://127.0.0.1:${server.address().port}/v1`,
|
||||
docs: (collection) => new Map([...docs(collection)].map(([id, doc]) => [id, publicDoc(doc)])),
|
||||
seed,
|
||||
/** Aufgezeichnete Listenabfragen (nur GET auf eine Collection, auch fehlgeschlagene), je Aufruf eine Kopie; optional einer Collection */
|
||||
listCalls: (collection = null) => structuredClone(listLog.filter((call) => !collection || call.collection === collection)),
|
||||
resetListCalls: () => {
|
||||
listLog = []
|
||||
},
|
||||
/** Naechste passende Dokument-Anfrage scheitert mit status (einmalig); after passende Anfragen gehen vorher durch. */
|
||||
failNext: ({ collection = null, method = null, status = 500, message = '', after = 0 }) => {
|
||||
pendingFailure = { collection, method, status, message, after }
|
||||
|
||||
Reference in new issue
Block a user