Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ jobs:
try-scenario:
- ember-lts-3.28-and-ember-data-3.28
- ember-lts-4.4-and-ember-data-4.4
- ember-release
- ember-beta
- ember-canary
- ember-lts-5.12-and-ember-data-5.3
- ember-6.2-and-ember-data-5.3
- ember-classic
# - ember-release
# - ember-beta
# - ember-canary
# - embroider-safe
# - embroider-optimized

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ Install **Ember-model-validator** is easy as:
or
`yarn add ember-model-validator --dev`

## Compatibility

* `ember-source` >= v3.28 and <= 6.2
* `ember-data` v3.28 or above

## Usage

**Ember-model-validator** provides a decorator to be included in your models for adding validation support. This decorator can be imported from your app's namespace (e.g. `import { modelValidator, objectValidator } from 'ember-model-validator';` in your models).
Expand Down Expand Up @@ -147,11 +152,6 @@ declare module 'ember-data/types/registries/model' {

```


## Compatibility

- `ember-source`and `ember-data` v3.28 or above

---

- [Ember model validator](#ember-model-validator)
Expand Down
6 changes: 4 additions & 2 deletions addon/decorators/core-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -470,15 +470,17 @@ function coreValidator(constructor) {
}
}
_validateRelations(property, validation) {
if (validation.relations.indexOf('hasMany') !== -1) {
const relationType = Array.isArray(validation.relations) ? validation.relations : validation.relations.value;

if (relationType.indexOf('hasMany') !== -1) {
if (get(this, `${property}.content`)) {
get(this, `${property}.content`).forEach((objRelation) => {
if (!objRelation.validate()) {
set(this, 'isValidNow', false);
}
});
}
} else if (validation.relations.indexOf('belongsTo') !== -1) {
} else if (relationType.indexOf('belongsTo') !== -1) {
if (get(this, `${property}.content`) && !get(this, `${property}.content`).validate()) {
set(this, 'isValidNow', false);
}
Expand Down
90 changes: 46 additions & 44 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,77 +36,79 @@
},
"dependencies": {
"ember-cli-babel": "^7.26.11",
"ember-cli-typescript": "^5.2.1"
"ember-cli-htmlbars": "6.3.0",
"ember-cli-typescript": "^5.3.0"
},
"devDependencies": {
"@ember/optional-features": "^2.0.0",
"@ember/test-helpers": "^2.8.1",
"@embroider/test-setup": "^3.0.1",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"@tsconfig/ember": "^2.0.0",
"@types/ember": "^4.0.3",
"@types/ember__application": "^4.0.5",
"@types/ember__array": "^4.0.3",
"@types/ember__component": "^4.0.11",
"@types/ember__controller": "^4.0.4",
"@types/ember__debug": "^4.0.3",
"@types/ember__destroyable": "^4.0.1",
"@types/ember__engine": "^4.0.4",
"@types/ember__error": "^4.0.2",
"@types/ember__object": "^4.0.5",
"@types/ember__polyfills": "^4.0.1",
"@types/ember__routing": "^4.0.12",
"@types/ember__runloop": "^4.0.2",
"@types/ember__service": "^4.0.2",
"@types/ember__string": "^3.0.10",
"@types/ember__template": "^4.0.1",
"@types/ember__test": "^4.0.1",
"@types/ember__test-helpers": "^2.9.1",
"@types/ember__utils": "^4.0.2",
"@types/ember-data": "4.4.7",
"@types/ember-data__adapter": "4.0.2",
"@types/ember-data__model": "^4.0.0",
"@types/ember-data__serializer": "^4.0.1",
"@types/ember-data__store": "^4.0.2",
"@types/ember-qunit": "^6.1.1",
"@types/ember-resolver": "^9.0.0",
"@types/qunit": "^2.19.3",
"@types/rsvp": "^4.0.4",
"@typescript-eslint/eslint-plugin": "5.54.0",
"@typescript-eslint/parser": "5.57.0",
"@tsconfig/ember": "^3.0.10",
"@types/ember": "^4.0.11",
"@types/ember__application": "^4.0.11",
"@types/ember__array": "^4.0.10",
"@types/ember__component": "^4.0.22",
"@types/ember__controller": "^4.0.12",
"@types/ember__debug": "^4.0.8",
"@types/ember__destroyable": "^4.0.5",
"@types/ember__engine": "^4.0.11",
"@types/ember__error": "^4.0.6",
"@types/ember__object": "^4.0.12",
"@types/ember__polyfills": "^4.0.6",
"@types/ember__routing": "^4.0.22",
"@types/ember__runloop": "^4.0.10",
"@types/ember__service": "^4.0.9",
"@types/ember__string": "^3.16.3",
"@types/ember__template": "^4.0.7",
"@types/ember__test": "^4.0.6",
"@types/ember__test-helpers": "^2.9.3",
"@types/ember__utils": "^4.0.7",
"@types/ember-data": "4.4.16",
"@types/ember-data__adapter": "4.0.6",
"@types/ember-data__model": "^4.0.5",
"@types/ember-data__serializer": "^4.0.6",
"@types/ember-data__store": "^4.0.7",
"@types/ember-resolver": "^9.0.2",
"@types/qunit": "^2.19.12",
"@types/rsvp": "^4.0.9",
"@typescript-eslint/eslint-plugin": "5.62.0",
"@typescript-eslint/parser": "5.62.0",
"auto-changelog": "^2.4.0",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^8.0.1",
"ember-auto-import": "^2.5.0",
"ember-cli": "~4.9.2",
"ember-cli-dependency-checker": "^3.3.1",
"ember-cli-htmlbars": "6.3.0",
"ember-auto-import": "^2.10.0",
"ember-cli": "~4.12.3",
"ember-cli-dependency-checker": "^3.3.3",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.2",
"ember-data": "^5.3.0",
"ember-data": "^5.3.12",
"ember-load-initializers": "^2.1.2",
"ember-page-title": "^7.0.0",
"ember-qunit": "^6.0.0",
"ember-qunit": "^6.2.0",
"ember-resolver": "^11.0.1",
"ember-source": "4.12.3",
"ember-source": "4.12.4",
"ember-source-channel-url": "^3.0.0",
"ember-template-lint": "^5.2.0",
"ember-try": "^2.0.0",
"eslint": "^8.43.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-ember": "^11.4.0",
"eslint-plugin-ember": "^11.12.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-qunit": "^7.3.4",
"loader.js": "^4.7.0",
"prettier": "^2.8.1",
"qunit": "^2.19.3",
"prettier": "^2.8.8",
"qunit": "^2.24.1",
"qunit-dom": "^2.0.0",
"release-it": "^15.6.0",
"typescript": "^5.2.2",
"webpack": "^5.75.0"
"typescript": "^5.8.2",
"webpack": "^5.98.0"
},
"resolutions": {
"@ember/test-waiters": "3.1.0"
},
"publishConfig": {
"access": "public",
Expand Down
19 changes: 19 additions & 0 deletions tests/dummy/app/models/fake-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class FakeModel extends Model {
@belongsTo('other-model', { async: true, inverse: null }) declare otherFake: AsyncBelongsTo<OtherModel>;
@belongsTo('async-model', { async: true, inverse: 'fakeModel' }) declare asyncModel: AsyncModel;

@belongsTo('other-model', { async: true, inverse: null }) declare ifOtherFake: AsyncBelongsTo<OtherModel>;
@hasMany('other-model', { async: true, inverse: null }) declare ifOtherFakes: AsyncHasMany<OtherModel>;

@attr('date', {
defaultValue() {
return new Date();
Expand Down Expand Up @@ -235,6 +238,22 @@ class FakeModel extends Model {
presence: true,
relations: ['belongsTo'],
},
ifOtherFake: {
relations: {
if: function (key: string, value: any, _this: FakeModel) {
return 'gallery' === _this.get('condType');
},
value: ['belongsTo'],
},
},
ifOtherFakes: {
relations: {
if: function (key: string, value: any, _this: FakeModel) {
return 'gallery' === _this.get('condType');
},
value: ['hasMany'],
},
},
otherCustomValidation: {
custom: {
validation: function (key: string, value: any) {
Expand Down
52 changes: 35 additions & 17 deletions tests/dummy/config/ember-try.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const getChannelURL = require('ember-source-channel-url');
// const getChannelURL = require('ember-source-channel-url');
// const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup');

module.exports = async function () {
Expand All @@ -26,33 +26,51 @@ module.exports = async function () {
},
},
{
name: 'ember-release',
name: 'ember-lts-5.12-and-ember-data-5.3',
npm: {
devDependencies: {
'ember-source': await getChannelURL('release'),
'ember-data': 'lts',
'ember-source': '~5.12.0',
'ember-data': '~5.3.0',
},
},
},
{
name: 'ember-beta',
name: 'ember-6.2-and-ember-data-5.3',
npm: {
devDependencies: {
'ember-source': await getChannelURL('beta'),
'ember-data': 'beta',
},
},
},
{
name: 'ember-canary',
allowedToFail: true,
npm: {
devDependencies: {
'ember-source': await getChannelURL('canary'),
'ember-data': 'canary',
'ember-source': '~6.2.0',
'ember-data': '~5.3.0',
},
},
},
// {
// name: 'ember-release',
// npm: {
// devDependencies: {
// 'ember-source': await getChannelURL('release'),
// 'ember-data': 'lts',
// },
// },
// },
// {
// name: 'ember-beta',
// npm: {
// devDependencies: {
// 'ember-source': await getChannelURL('beta'),
// 'ember-data': 'beta',
// },
// },
// },
// {
// name: 'ember-canary',
// allowedToFail: true,
// npm: {
// devDependencies: {
// 'ember-source': await getChannelURL('canary'),
// 'ember-data': 'canary',
// },
// },
// },
{
name: 'ember-classic',
env: {
Expand Down
2 changes: 1 addition & 1 deletion tests/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
setupApplicationTest as upstreamSetupApplicationTest,
setupRenderingTest as upstreamSetupRenderingTest,
setupTest as upstreamSetupTest,
SetupTestOptions,
type SetupTestOptions,
} from 'ember-qunit';

// This file exists to provide wrappers around ember-qunit's / ember-mocha's
Expand Down
64 changes: 64 additions & 0 deletions tests/unit/models/fake-model-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,44 @@ module('Unit | Model | fake-model', function (hooks) {

assert.false(model.validate({ only: ['otherFakes'] }));
});

module('conditional option', function () {
test('it validates only if `if function` returns true', async function (assert) {
assert.expect(1);

const store = this.owner.lookup('service:store');
const model = store.createRecord('fake-model', { condType: 'gallery' });

let ifOtherFakes = await model.ifOtherFakes;
const otherFake = store.createRecord('other-model');
if (ifOtherFakes.push) {
ifOtherFakes.push(otherFake);
} else {
ifOtherFakes.pushObject(otherFake);
}

assert.false(model.validate({ only: ['ifOtherFakes'] }));
});

test('skips validation if `if function` returns false', async function (assert) {
assert.expect(1);

const store = this.owner.lookup('service:store');
const model = store.createRecord('fake-model', { condType: 'chancuncha' });

let ifOtherFakes = await model.ifOtherFakes;
const otherFake = store.createRecord('other-model');
if (ifOtherFakes.push) {
ifOtherFakes.push(otherFake);
} else {
ifOtherFakes.pushObject(otherFake);
}

model.validate();

assert.strictEqual(model.get('errors').errorsFor('ifOtherFakes').length, 0);
});
});
});

module('`belongsTo` relations', function () {
Expand All @@ -635,6 +673,32 @@ module('Unit | Model | fake-model', function (hooks) {
Messages.presenceMessage
);
});

module('conditional option', function () {
test('it validates only if `if function` returns true', async function (assert) {
assert.expect(1);

const store = this.owner.lookup('service:store');
const model = store.createRecord('fake-model', { condType: 'gallery' });

model.set('ifOtherFake', store.createRecord('other-model'));

assert.false(model.validate({ only: ['ifOtherFake'] }));
});

test('skips validation if `if function` returns false', async function (assert) {
assert.expect(1);

const store = this.owner.lookup('service:store');
const model = store.createRecord('fake-model', { condType: 'chancuncha' });

model.set('ifOtherFake', store.createRecord('other-model'));

model.validate();

assert.strictEqual(model.get('errors').errorsFor('ifOtherFake').length, 0);
});
});
});
});

Expand Down
Loading
Loading