fql-toolkit
Version:
160 lines • 6.19 kB
JavaScript
import FQLQueryBuilder from './builders.js';
import FQLError from './errors.js';
import { buildCondition, mergeConditions } from './conditions.js';
// ---------------------------------------------------------------------------
// FormQuery
// ---------------------------------------------------------------------------
/**
* A chainable query builder for case (record) operations on a specific form.
* Returned by `client.form('name').labels()` or `.with()`.
*
* @example
* await client.form('customers').labels('name', 'email').with('age', '>', 18).get();
* await client.form('customers').with('name', '=', 'John').remove();
* await client.form('customers').with('name', '=', 'John').modify({ name: 'Jane', email: 'jane@example.com', age: 31 });
*/
class FormQuery {
constructor(formName, httpClient) {
this._formName = formName;
this._http = httpClient;
this._fields = [];
this._conditions = [];
}
/**
* Specifies which fields to return.
* May be called multiple times; fields accumulate.
*/
labels(...fields) {
this._fields.push(...fields.flat());
return this;
}
/**
* Adds a filter condition.
* Multiple calls are combined with AND.
*
* @param field - Field path, e.g. `'age'` or `'owner.name'`
* @param operator - One of: `=` `!=` `<>` `<` `>` `<=` `>=`
* @param value - Value to compare against
*/
with(field, operator, value) {
this._conditions.push(buildCondition(field, operator, value));
return this;
}
_condition() {
return mergeConditions(this._conditions);
}
/** Executes the query and returns matching records. */
async get() {
const query = FQLQueryBuilder.buildGetCaseQuery(this._formName, this._fields, this._condition());
return this._http.executeFQL(query);
}
/**
* Modifies all records matching the current conditions.
*
* Internally this method:
* 1. Fetches matching records to obtain their `fql_token`(s).
* 2. Calls `modify case` for each record.
*
* ⚠️ Values must be provided in the same order as the form's field definitions.
* Include ALL fields, not just the ones changing.
*
* @param newValues - `{ fieldName: newValue, ... }` in form-field order
*/
async modify(newValues = {}) {
if (this._conditions.length === 0) {
throw new FQLError('modify() requires at least one .with() condition');
}
const getQuery = FQLQueryBuilder.buildGetCaseQuery(this._formName, [], this._condition());
const getResult = await this._http.executeFQL(getQuery);
if (!getResult.ok)
return getResult;
const fqlToken = getResult.fqlToken;
if (!fqlToken)
return getResult;
const values = Object.values(newValues);
const modifyQuery = FQLQueryBuilder.buildModifyCaseQuery(this._formName, values);
return this._http.executeFQL(modifyQuery, fqlToken);
}
/**
* Removes all records matching the current conditions.
* At least one `.with()` condition is required.
*
* Internally this method:
* 1. Fetches matching records to obtain their `fql_token`.
* 2. Calls `remove case` for the matched record using its token.
*/
async remove() {
if (this._conditions.length === 0) {
throw new FQLError('remove() requires at least one .with() condition');
}
const getQuery = FQLQueryBuilder.buildGetCaseQuery(this._formName, [], this._condition());
const getResult = await this._http.executeFQL(getQuery);
if (!getResult.ok)
return getResult;
const fqlToken = getResult.fqlToken;
if (!fqlToken)
return getResult;
const removeQuery = FQLQueryBuilder.buildRemoveCaseQuery(this._formName);
return this._http.executeFQL(removeQuery, fqlToken);
}
}
// ---------------------------------------------------------------------------
// FormContext
// ---------------------------------------------------------------------------
/**
* Entry point for case (record) operations on a single form.
* Accessed via `client.form('formName')`.
*
* @example
* await client.form('customers').create({ name: 'John', email: 'john@example.com', age: 30 });
* await client.form('customers').labels('name', 'email').with('age', '>', 18).get();
* await client.form('customers').with('name', '=', 'John').remove();
* await client.form('customers').with('name', '=', 'John').modify({ name: 'Jane', email: 'jane@example.com', age: 31 });
*/
export default class FormContext {
constructor(formName, httpClient) {
if (!formName || typeof formName !== 'string') {
throw new FQLError('Form name is required and must be a string');
}
this._formName = formName;
this._http = httpClient;
}
/**
* Inserts a new record.
* Values are taken from the object in insertion order and must match
* the form's field definition order.
*/
async create(data = {}) {
const values = Object.values(data);
if (values.length === 0) {
throw new FQLError('create() requires at least one field value');
}
const query = FQLQueryBuilder.buildCreateNewQuery(this._formName, values);
return this._http.executeFQL(query);
}
/**
* Fetches all records from the form without any conditions or label filters.
*
* @example
* await client.form('Boards').get();
*/
get() {
const code = `get ${this._formName}`;
return this._http.executeFQL(code);
}
/**
* Starts a query and specifies which fields to return.
* @returns {FormQuery}
*/
labels(...fields) {
return new FormQuery(this._formName, this._http).labels(...fields);
}
/**
* Starts a query with a filter condition.
* @returns {FormQuery}
*/
with(field, operator, value) {
return new FormQuery(this._formName, this._http).with(field, operator, value);
}
}
//# sourceMappingURL=FormContext.js.map