@cedarjs/record 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,138 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var RelationProxy_exports = {};
29
+ __export(RelationProxy_exports, {
30
+ default: () => RelationProxy
31
+ });
32
+ module.exports = __toCommonJS(RelationProxy_exports);
33
+ var import_Reflection = __toESM(require("./Reflection"), 1);
34
+ class RelationProxy {
35
+ static addRelations(record) {
36
+ const reflection = new import_Reflection.default(record.constructor);
37
+ this.#addHasManyRelations(record, reflection.hasMany);
38
+ this.#addBelongsToRelations(record, reflection.belongsTo);
39
+ }
40
+ static #addHasManyRelations(record, hasMany) {
41
+ for (const [name, options] of Object.entries(hasMany)) {
42
+ if (record.hasOwnProperty(name)) {
43
+ continue;
44
+ }
45
+ const model = record.constructor.requiredModels.find((requiredModel) => {
46
+ return requiredModel.name === options.modelName;
47
+ });
48
+ if (!model) {
49
+ console.warn(
50
+ `Model ${record.constructor.name} has a relationship defined for \`${name}\` in schema.prisma, but there is no Redwoood model for this relationship.`
51
+ );
52
+ continue;
53
+ }
54
+ Object.defineProperty(record, name, {
55
+ get() {
56
+ if (options.foreignKey === null) {
57
+ return new RelationProxy(model, {
58
+ where: {
59
+ [options.referenceName]: {
60
+ some: { [options.primaryKey]: record[options.primaryKey] }
61
+ }
62
+ },
63
+ create: {
64
+ [options.referenceName]: {
65
+ connect: [
66
+ { [options.primaryKey]: record[options.primaryKey] }
67
+ ]
68
+ }
69
+ }
70
+ });
71
+ } else {
72
+ return new RelationProxy(model, {
73
+ where: { [options.foreignKey]: record[options.primaryKey] }
74
+ });
75
+ }
76
+ },
77
+ enumerable: true
78
+ });
79
+ }
80
+ }
81
+ static #addBelongsToRelations(record, belongsTo) {
82
+ for (const [name, options] of Object.entries(belongsTo)) {
83
+ if (record.hasOwnProperty(name)) {
84
+ continue;
85
+ }
86
+ const model = record.constructor.requiredModels.find((requiredModel) => {
87
+ return requiredModel.name === options.modelName;
88
+ });
89
+ if (!model) {
90
+ console.warn(
91
+ `Model ${record.constructor.name} has a relationship defined for \`${name}\` in schema.prisma, but there is no Redwoood model for this relationship.`
92
+ );
93
+ continue;
94
+ }
95
+ Object.defineProperty(record, name, {
96
+ async get() {
97
+ return await model.findBy({
98
+ [options.primaryKey]: record[options.foreignKey]
99
+ });
100
+ },
101
+ enumerable: true
102
+ });
103
+ }
104
+ }
105
+ constructor(model, relation) {
106
+ this.model = model;
107
+ this.relation = relation;
108
+ }
109
+ all(...args) {
110
+ return this.where(...args);
111
+ }
112
+ create(attributes, options = {}) {
113
+ let relatedAttributes = { ...attributes };
114
+ if (this.relation.create) {
115
+ relatedAttributes = { ...relatedAttributes, ...this.relation.create };
116
+ } else {
117
+ relatedAttributes = { ...relatedAttributes, ...this.relation.where };
118
+ }
119
+ return this.model.create(relatedAttributes, options);
120
+ }
121
+ find(id, options = {}) {
122
+ return this.findBy({ [this.model.primaryKey]: id }, options);
123
+ }
124
+ findBy(attributes, options = {}) {
125
+ const relatedAttributes = {
126
+ ...attributes,
127
+ ...this.relation.where
128
+ };
129
+ return this.model.findBy(relatedAttributes, options);
130
+ }
131
+ first(...args) {
132
+ return this.findBy(...args);
133
+ }
134
+ where(attributes, options = {}) {
135
+ const relatedAttributes = { ...attributes, ...this.relation.where };
136
+ return this.model.where(relatedAttributes, options);
137
+ }
138
+ }
@@ -0,0 +1,92 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var ValidationMixin_exports = {};
19
+ __export(ValidationMixin_exports, {
20
+ default: () => ValidationMixin_default
21
+ });
22
+ module.exports = __toCommonJS(ValidationMixin_exports);
23
+ var import_api = require("@cedarjs/api");
24
+ var ValidationMixin_default = (Base) => class extends Base {
25
+ // Stores error messages internally
26
+ _errors = { base: [] };
27
+ // Removes all error messages.
28
+ _clearErrors() {
29
+ for (const [attribute, _array] of Object.entries(this._errors)) {
30
+ this._errors[attribute] = [];
31
+ }
32
+ }
33
+ // Denotes validations that need to run for the given fields. Must be in the
34
+ // form of { field: options } where `field` is the name of the field and
35
+ // `options` are the validation options. See Service Validations docs for
36
+ // usage examples: https://redwoodjs.com/docs/services.html#service-validations
37
+ //
38
+ // static validates = {
39
+ // emailAddress: { email: true },
40
+ // name: { presence: true, length: { min: 2, max: 255 } }
41
+ // }
42
+ static validates = {};
43
+ // Whether or not this instance is valid and has no errors. Essentially the
44
+ // opposite of `hasError`, but runs validations first. This means it will
45
+ // reset any custom errors added with `addError()`
46
+ get isValid() {
47
+ this.validate();
48
+ return !this.hasError;
49
+ }
50
+ get errors() {
51
+ return this._errors;
52
+ }
53
+ // Whether or not this instance contains any errors according to validation
54
+ // rules. Does not run valiations, (and so preserves custom errors) returns
55
+ // the state of error objects. Essentially the opposite of `isValid`.
56
+ get hasError() {
57
+ return !Object.entries(this._errors).every(
58
+ ([_name, errors]) => !errors.length
59
+ );
60
+ }
61
+ // Adds an error to the _errors object. Can be called manually via instance,
62
+ // however any errors added this way will be wiped out if calling `validate()`
63
+ addError(attribute, message) {
64
+ if (!this._errors[attribute]) {
65
+ this._errors[attribute] = [];
66
+ }
67
+ this._errors[attribute].push(message);
68
+ }
69
+ // Checks each field against validate directives. Creates errors if so and
70
+ // returns `false`, otherwise returns `true`.
71
+ validate(options = {}) {
72
+ this._clearErrors();
73
+ if (this.constructor.validates.length === 0) {
74
+ return true;
75
+ }
76
+ const results = [];
77
+ for (const [name, recipe] of Object.entries(this.constructor.validates)) {
78
+ try {
79
+ (0, import_api.validate)(this[name], name, recipe);
80
+ results.push(true);
81
+ } catch (e) {
82
+ this.addError(name, e.message);
83
+ if (options.throw) {
84
+ throw e;
85
+ } else {
86
+ results.push(false);
87
+ }
88
+ }
89
+ }
90
+ return results.every((result) => result);
91
+ }
92
+ };
@@ -0,0 +1,121 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var parse_exports = {};
29
+ __export(parse_exports, {
30
+ parseDatamodel: () => parseDatamodel
31
+ });
32
+ module.exports = __toCommonJS(parse_exports);
33
+ var import_fs = __toESM(require("fs"), 1);
34
+ var import_path = __toESM(require("path"), 1);
35
+ var import_internals = require("@prisma/internals");
36
+ var esbuild = __toESM(require("esbuild"), 1);
37
+ var import_project_config = require("@cedarjs/project-config");
38
+ const DATAMODEL_PATH = import_path.default.join((0, import_project_config.getPaths)().api.models, "datamodel.js");
39
+ const MODELS_PATH = import_path.default.join((0, import_project_config.getPaths)().api.src, "models");
40
+ const MODELS_INDEX_PATH = import_path.default.join(MODELS_PATH, "index.js");
41
+ const indexLines = [
42
+ "// This file is autogenerated by Redwood and will be overwritten periodically",
43
+ "",
44
+ "import { db } from 'src/lib/db'",
45
+ "import datamodel from 'src/models/datamodel'",
46
+ "import { RedwoodRecord } from '@cedarjs/record'",
47
+ "",
48
+ "RedwoodRecord.db = db",
49
+ "RedwoodRecord.schema = datamodel",
50
+ ""
51
+ ];
52
+ const modelImports = [];
53
+ const modelRequires = {};
54
+ let datamodel;
55
+ const parseDatamodel = () => {
56
+ const schema = (0, import_internals.getSchema)((0, import_project_config.getPaths)().api.dbSchema);
57
+ (0, import_internals.getDMMF)({ datamodel: schema }).then((schema2) => {
58
+ datamodel = schema2.datamodel;
59
+ try {
60
+ const dir = import_path.default.dirname(DATAMODEL_PATH);
61
+ if (!import_fs.default.existsSync(dir)) {
62
+ import_fs.default.mkdirSync(dir, { recursive: true });
63
+ }
64
+ import_fs.default.writeFileSync(
65
+ DATAMODEL_PATH,
66
+ esbuild.transformSync(JSON.stringify(datamodel, null, 2), {
67
+ loader: "json",
68
+ format: "cjs"
69
+ }).code
70
+ );
71
+ console.info(`
72
+ Wrote ${DATAMODEL_PATH}`);
73
+ } catch (e) {
74
+ console.error("Error writing datamodel to", DATAMODEL_PATH);
75
+ }
76
+ const modelNames = import_fs.default.readdirSync(MODELS_PATH).map((file) => {
77
+ if (file !== "index.js" && file !== "datamodel.js") {
78
+ return file.split(".")[0];
79
+ }
80
+ }).filter((val) => val);
81
+ if (modelNames.length === 0) {
82
+ console.warn("No models found in", MODELS_PATH);
83
+ console.warn(
84
+ "Please create a model to represent the database table you want to access."
85
+ );
86
+ return;
87
+ }
88
+ modelNames.forEach((modelName) => {
89
+ const thisModelRequires = [];
90
+ modelImports.push(`import ${modelName} from 'src/models/${modelName}'`);
91
+ const schemaModel = datamodel.models.find(
92
+ (model) => model.name === modelName
93
+ );
94
+ if (schemaModel) {
95
+ schemaModel.fields.forEach((field) => {
96
+ if (field.kind === "object" && modelNames.includes(field.type)) {
97
+ thisModelRequires.push(field.type);
98
+ }
99
+ });
100
+ modelRequires[modelName] = thisModelRequires;
101
+ }
102
+ });
103
+ modelImports.forEach((modelImport) => {
104
+ indexLines.push(modelImport);
105
+ });
106
+ indexLines.push("");
107
+ for (const [name, requires] of Object.entries(modelRequires)) {
108
+ indexLines.push(`${name}.requiredModels = [${requires.join(", ")}]`);
109
+ }
110
+ indexLines.push("");
111
+ indexLines.push(`export { ${modelNames.join(", ")} }`);
112
+ indexLines.push("");
113
+ import_fs.default.writeFileSync(MODELS_INDEX_PATH, indexLines.join("\n"));
114
+ console.info(` Wrote ${MODELS_INDEX_PATH}
115
+ `);
116
+ });
117
+ };
118
+ // Annotate the CommonJS export names for ESM import in node:
119
+ 0 && (module.exports = {
120
+ parseDatamodel
121
+ });
package/dist/errors.js ADDED
@@ -0,0 +1,47 @@
1
+ import { RedwoodError } from "@cedarjs/api";
2
+ class RedwoodRecordError extends RedwoodError {
3
+ constructor() {
4
+ super();
5
+ this.name = "RedwoodRecordError";
6
+ }
7
+ }
8
+ class RedwoodRecordUncaughtError extends RedwoodError {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "RedwoodRecordUncaughtError";
12
+ }
13
+ }
14
+ class RedwoodRecordNotFoundError extends RedwoodError {
15
+ constructor(name) {
16
+ super(`${name} record not found`);
17
+ this.name = "RedwoodRecordNotFoundError";
18
+ }
19
+ }
20
+ class RedwoodRecordNullAttributeError extends RedwoodError {
21
+ constructor(name) {
22
+ super(`${name} must not be null`);
23
+ this.name = "RedwoodRecordNullAttributeError";
24
+ }
25
+ }
26
+ class RedwoodRecordMissingAttributeError extends RedwoodError {
27
+ constructor(name) {
28
+ super(`${name} is missing`);
29
+ this.name = "RedwoodRecordMissingAttributeError";
30
+ }
31
+ }
32
+ class RedwoodRecordMissingRequiredModelError extends RedwoodError {
33
+ constructor(modelName, requiredModelName) {
34
+ super(
35
+ `Tried to build a relationship for ${requiredModelName} model but is not listed as a \`requiredModel\` in ${modelName}`
36
+ );
37
+ this.name = "RedwoodRecordMissingRequiredModelError";
38
+ }
39
+ }
40
+ export {
41
+ RedwoodRecordError,
42
+ RedwoodRecordMissingAttributeError,
43
+ RedwoodRecordMissingRequiredModelError,
44
+ RedwoodRecordNotFoundError,
45
+ RedwoodRecordNullAttributeError,
46
+ RedwoodRecordUncaughtError
47
+ };
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ export * from "./errors";
2
+ import { default as default2 } from "./redwoodrecord/Core";
3
+ import { default as default3 } from "./redwoodrecord/Reflection";
4
+ import { default as default4 } from "./redwoodrecord/RelationProxy";
5
+ import { default as default5 } from "./redwoodrecord/ValidationMixin";
6
+ import { default as default6 } from "./redwoodrecord/RedwoodRecord";
7
+ export * from "./tasks/parse";
8
+ export {
9
+ default2 as Core,
10
+ default6 as RedwoodRecord,
11
+ default3 as Reflection,
12
+ default4 as RelationProxy,
13
+ default5 as ValidationMixin
14
+ };
@@ -0,0 +1,236 @@
1
+ import camelCase from "camelcase";
2
+ import * as Errors from "../errors";
3
+ class Core {
4
+ ////////////////////////////
5
+ // Public class properties
6
+ ////////////////////////////
7
+ // Set in child class to override DB accessor name. This is the name of the
8
+ // property you would call on an instance of Prisma Client in order the query
9
+ // a model in your schema. i.e. For the call `db.user` the accessorName is
10
+ // "user". Not setting this property will use the default camelCase version of
11
+ // the class name itself as the accessor.
12
+ //
13
+ // static accessorName = 'users'
14
+ static accessorName;
15
+ // Set in child class to override primary key field name for this model.
16
+ //
17
+ // static primaryKey = 'userId'
18
+ static primaryKey = "id";
19
+ // Parsed schema.prisma for use in subclasses
20
+ static schema = null;
21
+ // Any other model that may be required by this model
22
+ static requiredModels = [];
23
+ /////////////////////////
24
+ // Public class methods
25
+ /////////////////////////
26
+ // Access the raw Prisma Client for doing low level query manipulation
27
+ static db = null;
28
+ // Returns the Prisma DB accessor instance (ex. db.user)
29
+ static get accessor() {
30
+ return this.db[this.accessorName || camelCase(this.name)];
31
+ }
32
+ // Alias for where({})
33
+ static all(args) {
34
+ return this.where({}, args);
35
+ }
36
+ static build(attributes) {
37
+ const record = new this();
38
+ record.attributes = attributes;
39
+ return record;
40
+ }
41
+ // Create a new record. Instantiates a new instance and then calls .save() on it
42
+ static async create(attributes, options = {}) {
43
+ const record = this.build(attributes);
44
+ return await record.save(options);
45
+ }
46
+ // Find a single record by ID.
47
+ static async find(id, options = {}) {
48
+ const record = await this.findBy(
49
+ {
50
+ [this.primaryKey]: id,
51
+ ...options.where || {}
52
+ },
53
+ options
54
+ );
55
+ if (record === null) {
56
+ throw new Errors.RedwoodRecordNotFoundError(this.name);
57
+ }
58
+ return record;
59
+ }
60
+ // Returns the first record matching the given `where`, otherwise first in the
61
+ // whole table (whatever the DB determines is the first record)
62
+ static async findBy(attributes, options = {}) {
63
+ const record = await this.accessor.findFirst({
64
+ where: attributes,
65
+ ...options
66
+ });
67
+ return record ? await this.build(record) : null;
68
+ }
69
+ // Alias for findBy
70
+ static async first(...args) {
71
+ return this.findBy(...args);
72
+ }
73
+ // Find all records
74
+ static async where(attributes, options = {}) {
75
+ const records = await this.accessor.findMany({
76
+ where: attributes,
77
+ ...options
78
+ });
79
+ return Promise.all(
80
+ records.map(async (record) => {
81
+ return await this.build(record);
82
+ })
83
+ );
84
+ }
85
+ ///////////////////////////////
86
+ // Private instance properties
87
+ ///////////////////////////////
88
+ // Stores instance attributes object internally
89
+ #attributes = {};
90
+ ////////////////////////////
91
+ // Public instance methods
92
+ ////////////////////////////
93
+ constructor() {
94
+ }
95
+ get attributes() {
96
+ return this.#attributes;
97
+ }
98
+ set attributes(attrs) {
99
+ if (attrs) {
100
+ this.#attributes = attrs;
101
+ this._createPropertiesForAttributes();
102
+ }
103
+ }
104
+ async destroy(options = {}) {
105
+ const { throw: shouldThrow, ...rest } = options;
106
+ try {
107
+ await this.constructor.accessor.delete({
108
+ where: { [this.constructor.primaryKey]: this.attributes.id },
109
+ ...rest
110
+ });
111
+ return this;
112
+ } catch (e) {
113
+ this._deleteErrorHandler(e, shouldThrow);
114
+ return false;
115
+ }
116
+ }
117
+ // Saves the attributes to the database
118
+ async save(options = {}) {
119
+ const saveAttributes = JSON.parse(JSON.stringify(this.attributes));
120
+ try {
121
+ let newAttributes;
122
+ if (this.attributes[this.constructor.primaryKey]) {
123
+ delete saveAttributes[this.constructor.primaryKey];
124
+ newAttributes = await this.constructor.accessor.update({
125
+ where: {
126
+ [this.constructor.primaryKey]: this.attributes[this.constructor.primaryKey]
127
+ },
128
+ data: saveAttributes
129
+ });
130
+ } else {
131
+ newAttributes = await this.constructor.accessor.create({
132
+ data: saveAttributes
133
+ });
134
+ }
135
+ this.attributes = newAttributes;
136
+ } catch (e) {
137
+ this._saveErrorHandler(e, options.throw);
138
+ return false;
139
+ }
140
+ return this;
141
+ }
142
+ async update(attributes = {}, options = {}) {
143
+ this.#attributes = Object.assign(this.#attributes, attributes);
144
+ return await this.save(options);
145
+ }
146
+ ////////////////////////////
147
+ // Private instance methods
148
+ ////////////////////////////
149
+ // Turns a plain object's properties into getters/setters on the instance:
150
+ //
151
+ // const user = new User({ name: 'Rob' })
152
+ // user.name // => 'Rob'
153
+ _createPropertiesForAttributes() {
154
+ for (const [name, value] of Object.entries(this.attributes)) {
155
+ if (this.hasOwnProperty(name)) {
156
+ continue;
157
+ }
158
+ if (value && typeof value === "object" && (Object.keys(value).includes("create") || Object.keys(value).includes("connect"))) {
159
+ continue;
160
+ }
161
+ this._createPropertyForAttribute(name);
162
+ }
163
+ }
164
+ // Create property for a single attribute
165
+ _createPropertyForAttribute(name) {
166
+ Object.defineProperty(this, name, {
167
+ get() {
168
+ return this._attributeGetter(name);
169
+ },
170
+ set(value) {
171
+ this._attributeSetter(name, value);
172
+ },
173
+ enumerable: true
174
+ });
175
+ }
176
+ _attributeGetter(name) {
177
+ return this.#attributes[name];
178
+ }
179
+ _attributeSetter(name, value) {
180
+ return this.#attributes[name] = value;
181
+ }
182
+ _onSaveError(_name, _message) {
183
+ }
184
+ _deleteErrorHandler(error, shouldThrow) {
185
+ if (error.message.match(/Record to delete does not exist/)) {
186
+ this._onSaveError(
187
+ "base",
188
+ `${this.constructor.name} record to destroy not found`
189
+ );
190
+ if (shouldThrow) {
191
+ throw new Errors.RedwoodRecordNotFoundError();
192
+ }
193
+ } else {
194
+ this._onSaveError(
195
+ "base",
196
+ `${this.constructor.name} record threw uncaught error: ${error.message}`
197
+ );
198
+ if (shouldThrow) {
199
+ throw new Errors.RedwoodRecordUncaughtError(error.message);
200
+ }
201
+ }
202
+ }
203
+ // Handles errors from saving a record (either update or create), converting
204
+ // to this.#errors messages, or throwing RedwoodRecord errors
205
+ _saveErrorHandler(error, shouldThrow) {
206
+ if (error.message.match(/Record to update not found/)) {
207
+ this._onSaveError(
208
+ "base",
209
+ `${this.constructor.name} record to update not found`
210
+ );
211
+ if (shouldThrow) {
212
+ throw new Errors.RedwoodRecordNotFoundError(this.constructor.name);
213
+ }
214
+ } else if (error.message.match(/must not be null/)) {
215
+ const [_all, name] = error.message.match(/Argument (\w+)/);
216
+ this._onSaveError(name, "must not be null");
217
+ if (shouldThrow) {
218
+ throw new Errors.RedwoodRecordNullAttributeError(name);
219
+ }
220
+ } else if (error.message.match(/is missing/)) {
221
+ const [_all, name] = error.message.match(/Argument (\w+)/);
222
+ this._onSaveError("base", `${name} is missing`);
223
+ if (shouldThrow) {
224
+ throw new Errors.RedwoodRecordMissingAttributeError(name);
225
+ }
226
+ } else {
227
+ this._onSaveError("base", error.message);
228
+ if (shouldThrow) {
229
+ throw new Errors.RedwoodRecordUncaughtError(error.message);
230
+ }
231
+ }
232
+ }
233
+ }
234
+ export {
235
+ Core as default
236
+ };
@@ -0,0 +1,36 @@
1
+ import Core from "./Core";
2
+ import Reflection from "./Reflection";
3
+ import RelationProxy from "./RelationProxy";
4
+ import ValidationMixin from "./ValidationMixin";
5
+ class RedwoodRecord extends ValidationMixin(Core) {
6
+ static get reflect() {
7
+ return new Reflection(this);
8
+ }
9
+ // Call original build, but add related attributes
10
+ static build(attributes) {
11
+ const record = super.build(attributes);
12
+ RelationProxy.addRelations(record, this.constructor.schema);
13
+ return record;
14
+ }
15
+ // Don't even try to save if data isn't valid
16
+ async save(options = {}) {
17
+ if (this.validate({ throw: options.throw })) {
18
+ return await super.save(options);
19
+ } else {
20
+ return false;
21
+ }
22
+ }
23
+ // Call original method, but add error keys for validation
24
+ _createPropertyForAttribute(name) {
25
+ super._createPropertyForAttribute(name);
26
+ this._errors[name] = [];
27
+ }
28
+ // Add validation error on save error
29
+ _onSaveError(...args) {
30
+ super._onSaveError(...args);
31
+ this.addError(...args);
32
+ }
33
+ }
34
+ export {
35
+ RedwoodRecord as default
36
+ };