Files
GDevelop/Extensions/Firebase/B_firebasetools/D_cloudfirestoretools.ts
D8H 752d047464 Upgrade GDJS to Prettier 3.4.2 (#7398)
- Don't show in changelog
2025-02-12 18:50:02 +01:00

607 lines
22 KiB
TypeScript

namespace gdjs {
export namespace evtTools {
export namespace firebaseTools {
/**
* Firebase Cloud Firestore Event Tools.
* @namespace
*/
export namespace firestore {
const queries = new Map<string, firebase.firestore.Query>();
/**
* Converts a firebase document snapshot to a plain dictionary,
* so that it may be serialized or converted to a {@link gdjs.Variable}.
*
* @param doc - The document snapshot.
* @returns - The converted object.
*/
const documentSnapshotToSerializable = (
doc: firebase.firestore.DocumentSnapshot
) => ({
data: doc.data(),
exists: doc.exists,
id: doc.id,
});
/**
* Converts a firebase query snapshot to a plain dictionary,
* so that it may be serialized or converted to a {@link gdjs.Variable}.
*
* @param query - The query snapshot.
* @returns - The converted object.
*/
const querySnapshotToSerializable = (
query: firebase.firestore.QuerySnapshot
) => ({
size: query.size,
empty: query.empty,
docs: query.docs.map(documentSnapshotToSerializable),
});
/**
* Initiate a query over a collection.
* @param queryID - The name of the new query.
* @param collectionName - The name of the collection to query.
*/
export const startQuery = (queryID: string, collectionName: string) => {
queries.set(queryID, firebase.firestore().collection(collectionName));
};
/**
* Create a new query from a base query.
* @param queryID - The name of the new query.
* @param sourceQueryID - The name of the source query.
*/
export const startQueryFrom = (
queryID: string,
sourceQueryID: string
) => {
if (queries.has(sourceQueryID))
queries.set(queryID, queries.get(sourceQueryID)!);
};
/**
* Filters out documents whose fields do not match a condition
* from a query.
* @param queryID - The query to add the filter to.
* @param field - The field to run the condition on.
* @param op - The condition operator.
* @param value - The value to check against.
*/
export const queryWhere = (
queryID: string,
field: string,
op: Exclude<
firebase.firestore.WhereFilterOp,
// Exclude unsupported "batch" operations (as they require an array as value to check)
'in' | 'array-contains-any' | 'not-in'
>,
value: string | number
) => {
if (queries.has(queryID))
queries.set(queryID, queries.get(queryID)!.where(field, op, value));
};
/**
* Orders the documents in a query.
*
* @param queryID - The query to add the filter to.
* @param field - The field to order by.
* @param direction - The direction of ordering (ascending or descending).
*/
export const queryOrderBy = (
queryID: string,
field: string,
direction: firebase.firestore.OrderByDirection
) => {
if (queries.has(queryID))
queries.set(
queryID,
queries.get(queryID)!.orderBy(field, direction)
);
};
/**
* Limits the amount of documents returned by the query.
*
* @param queryID - The query to add the filter to.
* @param amount - The amount of documents to limit to
* @param last - If true, limits to the last documents instead of the first documents.
*/
export const queryLimit = (
queryID: string,
amount: integer,
last: boolean
) => {
if (queries.has(queryID))
queries.set(
queryID,
queries.get(queryID)![last ? 'limitToLast' : 'limit'](amount)
);
};
/**
* Makes a query skip documents after or before a certain
* value of a field the query was ordered with.
*
* @param queryID - The query to add the filter to.
* @param value - The value of the field ordered by.
* @param before - If set to true, all documents before the document are skipped, else all documents after it are skipped.
* @param includeSelf - If set to true, doesn't skip the document.
*/
export const querySkipSome = (
queryID: string,
value: number,
before: boolean,
includeSelf: boolean
) => {
if (queries.has(queryID))
queries.set(
queryID,
queries
.get(queryID)!
[
before
? includeSelf
? 'endAt'
: 'endBefore'
: includeSelf
? 'startAt'
: 'startAfter'
](value)
);
};
/**
* Execute a query and store results in a callback variable.
*
* @param queryID - The query to execute.
* @param [callbackValueVariable] - The variable where to store the result.
* @param [callbackStatusVariable] - The variable where to store if the operation was successful.
*/
export const executeQuery = (
queryID: string,
callbackValueVariable?: gdjs.Variable,
callbackStatusVariable?: gdjs.Variable
) => {
if (!queries.has(queryID)) return;
queries
.get(queryID)!
.get()
.then((snapshot) => {
if (typeof callbackStatusVariable !== 'undefined')
callbackStatusVariable.setString('ok');
if (typeof callbackValueVariable !== 'undefined')
callbackValueVariable.fromJSObject(
querySnapshotToSerializable(snapshot)
);
})
.catch((error) => {
if (typeof callbackStatusVariable !== 'undefined')
callbackStatusVariable.setString(error.message);
});
};
/**
* Watch a query and store results in a callback
* variable whenever a documents starts/stops
* matching the query or a document matching
* the query is modified.
*
* @param queryID - The query to execute.
* @param [callbackValueVariable] - The variable where to store the result.
* @param [callbackStatusVariable] - The variable where to store if the operation was successful.
*/
export const watchQuery = (
queryID: string,
callbackValueVariable?: gdjs.Variable,
callbackStatusVariable?: gdjs.Variable
) => {
if (!queries.has(queryID)) return;
queries.get(queryID)!.onSnapshot(
(snapshot) => {
if (typeof callbackStatusVariable !== 'undefined')
callbackStatusVariable.setString('ok');
if (typeof callbackValueVariable !== 'undefined')
callbackValueVariable.fromJSObject(
querySnapshotToSerializable(snapshot)
);
},
(error) => {
if (typeof callbackStatusVariable !== 'undefined')
callbackStatusVariable.setString(error.message);
}
);
};
/**
* Adds a variable in a collection as document with a unique name.
* @param collectionName - The collection where to store the variable.
* @param variable - The variable to write.
* @param [callbackStateVariable] - The variable where to store the result.
*/
export const addDocument = (
collectionName: string,
variable: gdjs.Variable,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.add(replaceTimestampsInObject(variable.toJSObject()))
.then(() => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Writes a variable in a collection as document.
* @param collectionName - The collection where to store the variable.
* @param variableName - The name under which the variable will be saved (document name).
* @param variable - The variable to write.
* @param [callbackStateVariable] - The variable where to store the result.
*/
export const writeDocument = (
collectionName: string,
variableName: string,
variable: gdjs.Variable,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.doc(variableName)
.set(replaceTimestampsInObject(variable.toJSObject()))
.then(() => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Writes a field of a document.
* @param collectionName - The collection where to store the document.
* @param documentName - The name of the document where to write a field.
* @param field - The field where to write.
* @param value - The value to write.
* @param [callbackStateVariable] - The variable where to store the result.
* @param [merge] - Should the new field replace the document or be merged with the document?
*/
export const writeField = (
collectionName: string,
documentName: string,
field: string,
value: any,
callbackStateVariable?: gdjs.Variable,
merge: boolean = true
) => {
firebase
.firestore()
.collection(collectionName)
.doc(documentName)
.set({ [field]: replaceTimestampInString(value) }, { merge: merge })
.then(() => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Updates a variable/document.
* @param collectionName - The collection where the document is stored.
* @param variableName - The name under which the variable will be saved (document name).
* @param variable - The variable to update.
* @param [callbackStateVariable] - The variable where to store the result.
*/
export const updateDocument = (
collectionName: string,
variableName: string,
variable: gdjs.Variable,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.doc(variableName)
.update(replaceTimestampsInObject(variable.toJSObject()))
.then(() => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Updates a field of a document.
* @param collectionName - The collection where the document is stored.
* @param documentName - The name of the document where to update a field.
* @param field - The field where to update.
* @param value - The value to write.
* @param [callbackStateVariable] - The variable where to store the result.
*/
export const updateField = (
collectionName: string,
documentName: string,
field: string,
value: any,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.doc(documentName)
.update({ [field]: replaceTimestampInString(value) })
.then(() => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Deletes a document.
* @param collectionName - The collection where the document is stored.
* @param documentName - The name of the document to delete.
* @param [callbackStateVariable] - The variable where to store the result.
*/
export const deleteDocument = (
collectionName: string,
documentName: string,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.doc(documentName)
.delete()
.then(() => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Deletes a field of a document.
* @param collectionName - The collection where the document is stored.
* @param documentName - The name of the document where to delete a field.
* @param field - The field to delete.
* @param [callbackStateVariable] - The variable where to store the result.
*/
export const deleteField = (
collectionName: string,
documentName: string,
field: string,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.doc(documentName)
.update({ [field]: firebase.firestore.FieldValue.delete() })
.then(() => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Gets a document and store it in a variable.
* @param collectionName - The collection where the document is stored.
* @param documentName - The name of the document to get.
* @param [callbackValueVariable] - The variable where to store the result.
* @param [callbackStateVariable] - The variable where to store if the operation was successful.
*/
export const getDocument = (
collectionName: string,
documentName: string,
callbackValueVariable?: gdjs.Variable,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.doc(documentName)
.get()
.then((doc) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
if (callbackValueVariable)
callbackValueVariable.fromJSObject(doc.data());
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Gets a field of a document and store it in a variable.
* @param collectionName - The collection where the document is stored.
* @param documentName - The name of the document.
* @param field - The field to get.
* @param [callbackValueVariable] - The variable where to store the result.
* @param [callbackStateVariable] - The variable where to store if the operation was successful.
*/
export const getField = (
collectionName: string,
documentName: string,
field: string,
callbackValueVariable?: gdjs.Variable,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.doc(documentName)
.get()
.then((doc) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
if (callbackValueVariable)
callbackValueVariable.fromJSObject(doc.get(field));
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Checks for existence of a document.
* @param collectionName - The collection where the document is stored.
* @param documentName - The name of the document to check.
* @param [callbackValueVariable] - The variable where to store the result.
* @param [callbackStateVariable] - The variable where to store if the operation was successful.
*/
export const hasDocument = (
collectionName: string,
documentName: string,
callbackValueVariable?: gdjs.Variable,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.doc(documentName)
.get()
.then((doc) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
if (callbackValueVariable)
callbackValueVariable.setBoolean(doc.exists);
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Checks for existence of a field.
* @param collectionName - The collection where the document is stored.
* @param documentName - The name of the document.
* @param field - The field to check.
* @param [callbackValueVariable] - The variable where to store the result.
* @param [callbackStateVariable] - The variable where to store if the operation was successful.
*/
export const hasField = (
collectionName: string,
documentName: string,
field: string,
callbackValueVariable?: gdjs.Variable,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.doc(documentName)
.get()
.then((doc) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString('ok');
if (callbackValueVariable) {
const value = doc.get(field, { serverTimestamps: 'estimate' });
callbackValueVariable.setBoolean(
doc.exists && value !== undefined && value !== null
);
}
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Lists all the documents in a collection.
* @param collectionName - The collection where to count documents.
* @param [callbackValueVariable] - The variable where to store the result.
* @param [callbackStateVariable] - The variable where to store if the operation was successful.
*
* @deprecated Use a query without filters instead.
*/
export const listDocuments = (
collectionName: string,
callbackValueVariable?: gdjs.Variable,
callbackStateVariable?: gdjs.Variable
) => {
firebase
.firestore()
.collection(collectionName)
.get()
.then((snapshot) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(
snapshot.empty ? 'empty' : 'ok'
);
if (callbackValueVariable)
callbackValueVariable.fromJSObject(
snapshot.docs.map((doc) => doc.id)
);
})
.catch((error) => {
if (typeof callbackStateVariable !== 'undefined')
callbackStateVariable.setString(error.message);
});
};
/**
* Returns a special string replaced by a firebase serverTimestamp field value.
*/
export const getServerTimestamp = () =>
'[{__FIREBASE_SERVERSIDE_TIMESTAMP}]';
const replaceTimestampInString = (str: any) => {
if (str === '[{__FIREBASE_SERVERSIDE_TIMESTAMP}]')
return firebase.firestore.FieldValue.serverTimestamp();
else return str;
};
const replaceTimestampsInObject = (object: object): object => {
for (const i in object) {
const item = object[i];
if (typeof item === 'object') replaceTimestampsInObject(item);
else if (item === '[{__FIREBASE_SERVERSIDE_TIMESTAMP}]')
object[i] = firebase.firestore.FieldValue.serverTimestamp();
}
return object;
};
}
}
}
}