diff --git a/CHANGELOG.md b/CHANGELOG.md index fcecd13..fa62b72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Fix unnamed sub-schemas not being able to be normalized properly * Add `ForeignKey` field * Fix `Alchemy.Client.Schema.isSchema(value)` never returning true +* Add support for asynchronous custom schema values in the `Schema` field class ## 1.4.0-alpha.3 (2024-02-25) diff --git a/lib/app/helper_field/schema_field.js b/lib/app/helper_field/schema_field.js index 996be4f..29a0f90 100644 --- a/lib/app/helper_field/schema_field.js +++ b/lib/app/helper_field/schema_field.js @@ -115,7 +115,7 @@ SchemaField.setProperty(function requires_translating() { * @param {Object} record This *should* be the schema context (might not be the root) * @param {string} some_path Some path to a field in the wanted schema * - * @return {Schema} + * @return {Alchemy.Schema|Pledge} */ SchemaField.setMethod(function getSubschema(record, some_path) { @@ -142,7 +142,7 @@ SchemaField.setMethod(function getSubschema(record, some_path) { * @param {string} some_path Some path to a field in the wanted schema * @param {string} schema The schema path to resolve * - * @return {Schema} + * @return {Alchemy.Schema|Pledge} */ SchemaField.setMethod(function resolveSchemaPath(context_schema, record, field_path, schema_path) { @@ -221,11 +221,10 @@ SchemaField.setMethod(function resolveSchemaPath(context_schema, record, field_p /** * Get the subschema via an association - * @TODO: Work in progress! * * @author Jelle De Loecker * @since 0.2.0 - * @version 1.3.21 + * @version 1.4.0 * * @param {Object} record This *should* be the schema context (might not be the root) * @param {string} path The path inside the associated schema @@ -237,33 +236,33 @@ SchemaField.setMethod(function getSubschemaFromAssociation(record, path, associa let local_value = record[association.options.local_key]; - console.log('Getting schema from association:', association, 'with local value:', local_value); - if (!local_value) { return false; } const associated_model = alchemy.getModel(association.model_name); - console.log(' -- Model:', associated_model) - if (!associated_model) { return false; } return Pledge.Swift.execute(async () => { - let result = await associated_model.findByValues({ - [association.options.foreign_key]: local_value, - }); - - console.log('Gound...', result); + let result = await associated_model.resolveRemoteSchemaRequest( + this, + association.options.foreign_key, + local_value, + path + ); if (!result) { return null; } - console.log('Looking for', associated_model.schema); + // If it already is a schema, return that + if (Classes.Alchemy.Client.Schema.isSchema(result)) { + return result; + } let found_schema = this.resolveSchemaPath(associated_model.schema, result, path, path); @@ -308,7 +307,7 @@ SchemaField.setMethod(function _toDatasource(value, holder, datasource, callback * * @author Jelle De Loecker * @since 0.2.0 - * @version 1.3.1 + * @version 1.4.0 * * @param {Object} value Value of field, an object in this case * @param {Object} data The data object containing `value` @@ -334,6 +333,39 @@ SchemaField.setMethod(function _toDatasourceFromValue(value, holder, datasource, sub_schema = this.getSubschema(record); + if (Pledge.isThenable(sub_schema)) { + let pledge = new Pledge.Swift(); + + Pledge.Swift.done(sub_schema, (err, sub_schema) => { + if (err) { + pledge.reject(err); + return callback(err); + } + + pledge.resolve(this._toDatasourceFromValueWithSubSchema(value, holder, record, sub_schema, datasource, callback)); + }); + + return pledge; + } else { + return this._toDatasourceFromValueWithSubSchema(value, holder, record, sub_schema, datasource, callback); + } +}); + +/** + * Cast all the subschema values using their _toDatasource method + * + * @author Jelle De Loecker + * @since 0.2.0 + * @version 1.4.0 + * + * @param {Object} value Value of field, an object in this case + * @param {Object} data The data object containing `value` + * @param {Datasource} datasource The destination datasource + * + * @return {Object} + */ +SchemaField.setMethod(function _toDatasourceFromValueWithSubSchema(value, holder, record, sub_schema, datasource, callback) { + // If the sub schema has been found, return it now if (sub_schema) { return datasource.toDatasource(sub_schema, value, callback); @@ -371,7 +403,14 @@ SchemaField.setMethod(function _toDatasourceFromValue(value, holder, datasource, // Try getting the schema again sub_schema = that.getSubschema(record); - datasource.toDatasource(sub_schema, value, callback); + Pledge.Swift.done(sub_schema, (err, sub_schema) => { + + if (err) { + return callback(err); + } + + datasource.toDatasource(sub_schema, value, callback); + }); }); return; @@ -439,28 +478,6 @@ SchemaField.setMethod(function _toApp(query, options, value, callback) { */ SchemaField.setMethod(async function _toAppFromValue(query, options, value, callback) { - var that = this, - Dummy, - item, - name; - - // Don't get schema associated records if recursive is disabled - let get_associations_recursive_level = options.recursive; - - if (get_associations_recursive_level == null) { - if (this.options?.recursive != null) { - get_associations_recursive_level = this.options.recursive; - } else { - get_associations_recursive_level = 1; - } - } - - if (get_associations_recursive_level && Blast.isBrowser) { - // @TODO: this will mostly fail on the browser, so disable it for now. - // Maybe make it configurable later - get_associations_recursive_level = 0; - } - let record; if (options.parent_value) { @@ -488,6 +505,60 @@ SchemaField.setMethod(async function _toAppFromValue(query, options, value, call let sub_schema = this.getSubschema(record); + if (Pledge.isThenable(sub_schema)) { + + let pledge = new Pledge.Swift(); + + Pledge.Swift.done(sub_schema, (err, sub_schema) => { + + if (err) { + pledge.reject(err); + return callback(err); + } + + this._toAppFromValueWithSubSchema(query, options, value, sub_schema, record, callback); + }); + + return pledge; + } else { + return this._toAppFromValueWithSubSchema(query, options, value, sub_schema, record, callback); + } +}); + +/** + * Turn datasource data into app data + * + * @author Jelle De Loecker + * @since 0.2.0 + * @version 1.4.0 + * + * @param {Mixed} value + * @param {Function} callback + */ +SchemaField.setMethod(async function _toAppFromValueWithSubSchema(query, options, value, sub_schema, record, callback) { + + let that = this, + Dummy, + item, + name; + + // Don't get schema associated records if recursive is disabled + let get_associations_recursive_level = options.recursive; + + if (get_associations_recursive_level == null) { + if (this.options?.recursive != null) { + get_associations_recursive_level = this.options.recursive; + } else { + get_associations_recursive_level = 1; + } + } + + if (get_associations_recursive_level && Blast.isBrowser) { + // @TODO: this will mostly fail on the browser, so disable it for now. + // Maybe make it configurable later + get_associations_recursive_level = 0; + } + // If the sub schema has been found, return it now if (sub_schema) { let tasks = {}; diff --git a/lib/app/helper_model/model.js b/lib/app/helper_model/model.js index 0fd164a..41831e4 100644 --- a/lib/app/helper_model/model.js +++ b/lib/app/helper_model/model.js @@ -982,6 +982,38 @@ Model.setMethod(function findByPk(pk, options, callback) { return this.find('first', criteria, callback); }); +/** + * Return the context for resolving a remote schema request. + * The only thing we probably need to do is return a document. + * + * @deprecated + * + * @author Jelle De Loecker + * @since 1.4.0 + * @version 1.4.0 + * + * @param {Alchemy.Field.Schema} external_field + * @param {string} our_field_name + * @param {*} our_field_value + * @param {string} schema_path + * + * @return {Alchemy.Document|Object|Schema} + */ +Model.setMethod(async function resolveRemoteSchemaRequest(external_field, our_field_name, our_field_value, schema_path) { + + let doc = await this.findByValues({ + [our_field_name]: our_field_value, + }); + + if (!doc) { + return null; + } + + let found_schema = external_field.resolveSchemaPath(this.schema, doc, schema_path, schema_path); + + return found_schema; +}); + /** * Query the database by key-val attributes, return the first result *