magically-sdk
Version:
Official SDK for Magically - Build mobile apps with AI
308 lines (307 loc) • 11.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MagicallyData = void 0;
const APIClient_1 = require("./APIClient");
const utils_1 = require("./utils");
class MagicallyData {
constructor(config, auth) {
this.config = config;
this.auth = auth;
this.apiClient = new APIClient_1.APIClient(config, 'MagicallyData');
}
/**
* Check if a query is public (doesn't require authentication)
*/
isPublicQuery(filter) {
return filter?.isPublic === true;
}
/**
* Check if an aggregate pipeline is public (doesn't require authentication)
*/
isPublicAggregate(pipeline) {
return pipeline?.[0]?.$match?.isPublic === true;
}
/**
* Check if a raw operation is public (read-only with isPublic filter)
*/
isPublicRawOperation(operation, query) {
const readOperations = ['find', 'findOne', 'count', 'countDocuments', 'distinct'];
return readOperations.includes(operation) && query?.isPublic === true;
}
/**
* Query data from a collection with type safety
* @param collection - Collection name
* @param filter - MongoDB filter object (optional)
* @param options - Query options (sort, limit, skip)
*/
async query(collection, filter, options) {
try {
const token = await (0, utils_1.getAuthToken)(this.apiClient, this.auth);
const result = await this.apiClient.request(`/api/project/${this.config.projectId}/data/query`, {
method: 'POST',
body: {
collection,
filter: filter || {},
sort: options?.sort || {},
limit: options?.limit || 100,
skip: options?.skip || 0,
populate: options?.populate || [],
},
operation: `query:${collection}`
}, token);
return {
data: result.data || [],
total: result.total || 0
};
}
catch (error) {
throw error;
}
}
/**
* Insert data into a collection with type safety
*/
async insert(collection, data, options = {}) {
try {
const token = await (0, utils_1.getAuthToken)(this.apiClient, this.auth);
const result = await this.apiClient.request(`/api/project/${this.config.projectId}/data/insert`, {
method: 'POST',
body: {
collection,
data,
upsert: options.upsert || false,
},
operation: `insert:${collection}`
}, token);
return result.data;
}
catch (error) {
throw error;
}
}
/**
* Update existing data in a collection (fails if not found)
*/
async update(collection, filter, update) {
try {
const token = await (0, utils_1.getAuthToken)(this.apiClient, this.auth);
// Check if it's already a MongoDB update object
const isMongoUpdate = update.$set || update.$inc || update.$push ||
update.$pull || update.$unset || update.$addToSet ||
update.$rename || update.$min || update.$max ||
update.$mul || update.$currentDate;
// If plain data, wrap in $set. Otherwise use as-is
const updateObj = isMongoUpdate ? update : { $set: update };
const result = await this.apiClient.request(`/api/project/${this.config.projectId}/data/update`, {
method: 'POST',
body: {
collection,
filter,
update: updateObj
},
operation: `update:${collection}`
}, token);
if (!result.result?.value) {
throw new Error('Document not found for update');
}
return result.result.value;
}
catch (error) {
throw error;
}
}
/**
* Upsert data in a collection (update if exists, insert if not)
* @param collection - Collection name
* @param filter - Filter to find existing document
* @param data - Data to insert or update
* @returns The upserted document and whether it was inserted
*/
async upsert(collection, filter, data) {
try {
const token = await (0, utils_1.getAuthToken)(this.apiClient, this.auth);
const result = await this.apiClient.request(`/api/project/${this.config.projectId}/data/upsert`, {
method: 'POST',
body: {
collection,
filter,
data
},
operation: `upsert:${collection}`
}, token);
return {
data: result.data,
upserted: result.upserted || false
};
}
catch (error) {
throw error;
}
}
/**
* Update multiple documents in a collection
* @param collection - Collection name
* @param filter - MongoDB filter to find documents to update
* @param update - Update data or MongoDB update object
* @returns Number of matched and modified documents
*/
async updateMany(collection, filter, update) {
try {
const token = await (0, utils_1.getAuthToken)(this.apiClient, this.auth);
// Check if it's already a MongoDB update object
const isMongoUpdate = update.$set || update.$inc || update.$push ||
update.$pull || update.$unset || update.$addToSet ||
update.$rename || update.$min || update.$max ||
update.$mul || update.$currentDate;
// If plain data, wrap in $set. Otherwise use as-is
const updateObj = isMongoUpdate ? update : { $set: update };
const result = await this.apiClient.request(`/api/project/${this.config.projectId}/data/update`, {
method: 'POST',
body: {
collection,
filter,
update: updateObj,
options: { multi: true }
},
operation: `updateMany:${collection}`
}, token);
return {
matchedCount: result.matchedCount || 0,
modifiedCount: result.modifiedCount || 0
};
}
catch (error) {
throw error;
}
}
/**
* Count documents in a collection
* @param collection - Collection name
* @param filter - Optional MongoDB filter
* @returns Count of matching documents
*/
async count(collection, filter) {
try {
const token = await (0, utils_1.getAuthToken)(this.apiClient, this.auth);
const result = await this.apiClient.request(`/api/project/${this.config.projectId}/data/raw`, {
method: 'POST',
body: {
collection,
operation: 'countDocuments',
query: filter || {}
},
operation: `count:${collection}`
}, token);
return { count: result.result || 0 };
}
catch (error) {
throw error;
}
}
/**
* Delete data from a collection (deletes multiple documents)
*/
async delete(collection, filter) {
try {
const token = await (0, utils_1.getAuthToken)(this.apiClient, this.auth);
const result = await this.apiClient.request(`/api/project/${this.config.projectId}/data/delete`, {
method: 'POST',
body: {
collection,
filter,
options: { deleteMany: true }
},
operation: `delete:${collection}`
}, token);
return { deletedCount: result.result.deletedCount || 0 };
}
catch (error) {
throw error;
}
}
/**
* Delete a single document from a collection
* @param collection - Collection name
* @param filter - MongoDB filter to find the document to delete
* @returns Number of deleted documents (0 or 1)
*/
async deleteOne(collection, filter) {
try {
const token = await (0, utils_1.getAuthToken)(this.apiClient, this.auth);
const result = await this.apiClient.request(`/api/project/${this.config.projectId}/data/delete`, {
method: 'POST',
body: {
collection,
filter,
options: { deleteMany: false } // deleteOne
},
operation: `deleteOne:${collection}`
}, token);
return { deletedCount: result.result.deletedCount || 0 };
}
catch (error) {
throw error;
}
}
/**
* Delete multiple documents from a collection (alias for delete)
* @param collection - Collection name
* @param filter - MongoDB filter to find documents to delete
* @returns Number of deleted documents
*/
async deleteMany(collection, filter) {
return this.delete(collection, filter);
}
/**
* Run aggregation query with type safety
*/
async aggregate(collection, pipeline) {
try {
const token = await (0, utils_1.getAuthToken)(this.apiClient, this.auth);
const result = await this.apiClient.request(`/api/project/${this.config.projectId}/data/aggregate`, {
method: 'POST',
body: {
collection,
pipeline,
allowDiskUse: false
},
operation: `aggregate:${collection}`
}, token);
return result.data;
}
catch (error) {
throw error;
}
}
/**
* Run raw MongoDB operations with type safety and validation
*/
async raw(collection, operation, options = {}) {
// Validate allowed operations for security
const allowedOperations = [
'find', 'findOne', 'count', 'countDocuments', 'distinct',
'aggregate', 'findOneAndUpdate', 'updateOne', 'updateMany',
'deleteOne', 'deleteMany', 'insertOne', 'insertMany'
];
if (!allowedOperations.includes(operation)) {
throw new Error(`Operation '${operation}' is not allowed. Allowed operations: ${allowedOperations.join(', ')}`);
}
try {
const token = await (0, utils_1.getAuthToken)(this.apiClient, this.auth);
const result = await this.apiClient.request(`/api/project/${this.config.projectId}/data/raw`, {
method: 'POST',
body: {
collection,
operation,
...options
},
operation: `raw:${operation}:${collection}`
}, token);
return result.result;
}
catch (error) {
throw error;
}
}
}
exports.MagicallyData = MagicallyData;