Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
38 changes: 38 additions & 0 deletions addon/models/attachable-asset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import AttachableModel from './attachable';
import { attr } from '@ember-data/model';

/**
* Concrete polymorphic model for an Asset attached to a device.
*/
export default class AttachableAssetModel extends AttachableModel {
/** @ids */
@attr('string') category_uuid;
@attr('string') vendor_uuid;
@attr('string') warranty_uuid;
@attr('string') telematic_uuid;
@attr('string') current_place_uuid;
@attr('string') photo_uuid;

/** @attributes */
@attr('string') description;
@attr('string') code;
@attr('string') category_name;
@attr('string') vendor_name;
@attr('string') warranty_name;
@attr('string') current_location;
@attr('string') usage_type;
@attr('string') vin;
@attr('string') plate_number;
@attr('string') make;
@attr('string') model;
@attr('string') year;
@attr('string') color;
@attr('string') serial_number;
@attr('string') measurement_system;
@attr('string') odometer;
@attr('string') odometer_unit;
@attr('string') notes;
@attr('raw') capacity;
@attr('raw') specs;
@attr('raw') attributes;
}
50 changes: 50 additions & 0 deletions addon/models/attachable-vehicle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import AttachableModel from './attachable';
import { attr } from '@ember-data/model';
import { get, computed } from '@ember/object';
import config from 'ember-get-config';

/**
* Concrete polymorphic model for a Vehicle attached to a device.
*/
export default class AttachableVehicleModel extends AttachableModel {
/** @ids */
@attr('string') internal_id;
@attr('string') photo_uuid;
@attr('string') vendor_uuid;
@attr('string') category_uuid;
@attr('string') warranty_uuid;
@attr('string') telematic_uuid;

/** @attributes */
@attr('string', {
defaultValue: get(config, 'defaultValues.vehicleImage'),
})
photo_url;

@attr('string', {
defaultValue: get(config, 'defaultValues.vehicleAvatar'),
})
avatar_url;

@attr('string') avatar_value;
@attr('string') driver_name;
@attr('string') vendor_name;
@attr('string') make;
@attr('string') model;
@attr('string') model_type;
@attr('string') year;
@attr('string') trim;
@attr('string') color;
@attr('string') plate_number;
@attr('string') call_sign;
@attr('string') serial_number;
@attr('string') vin;
@attr('string') measurement_system;
@attr('string') odometer;
@attr('string') odometer_unit;
@attr('number') engine_hours;

@computed('year', 'make', 'model') get yearMakeModel() {
return [this.year, this.make, this.model].filter(Boolean).join(' ');
}
}
60 changes: 60 additions & 0 deletions addon/models/attachable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Model, { attr } from '@ember-data/model';
import { computed } from '@ember/object';
import { format as formatDate, isValid as isValidDate, formatDistanceToNow } from 'date-fns';

/**
* Abstract base model for resources a device can be attached to.
* Concrete types: attachable-vehicle, attachable-asset.
*/
export default class AttachableModel extends Model {
/** @ids */
@attr('string') uuid;
@attr('string') public_id;
@attr('string') company_uuid;

/** @attributes */
@attr('string') name;
@attr('string') display_name;
@attr('string') type;
@attr('string') status;
@attr('string') photo_url;
@attr('string') avatar_url;
@attr('boolean') online;
@attr('boolean') is_online;
@attr('point') location;
@attr('string') speed;
@attr('string') heading;
@attr('string') altitude;
@attr('raw') meta;

/** @dates */
@attr('date') deleted_at;
@attr('date') created_at;
@attr('date') updated_at;

/** @computed */
@computed('name', 'display_name', 'public_id') get displayName() {
return this.display_name || this.name || this.public_id;
}

@computed('updated_at') get updatedAgo() {
if (!isValidDate(this.updated_at)) {
return null;
}
return formatDistanceToNow(this.updated_at);
}

@computed('updated_at') get updatedAt() {
if (!isValidDate(this.updated_at)) {
return null;
}
return formatDate(this.updated_at, 'yyyy-MM-dd HH:mm');
}

@computed('updated_at') get updatedAtShort() {
if (!isValidDate(this.updated_at)) {
return null;
}
return formatDate(this.updated_at, 'dd, MMM');
}
}
9 changes: 9 additions & 0 deletions addon/models/device-event.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ export default class DeviceEventModel extends Model {
@attr('string') comment;
@attr('string') slug;
@attr('string') device_name;
@attr('string') device_id;
@attr('string') device_imei;
@attr('string') device_serial_number;
@attr('string') device_connection_status;
@attr('string') device_status;
@attr('string') device_photo_url;
@attr('string') telematic_uuid;
@attr('string') telematic_name;
@attr('object') provider_descriptor;

/** @dates */
@attr('date') occurred_at;
Expand Down
1 change: 1 addition & 0 deletions addon/models/device.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default class DeviceModel extends Model {
/** @relationships */
@belongsTo('telematic', { async: false }) telematic;
@belongsTo('warranty', { async: false }) warranty;
@belongsTo('attachable', { polymorphic: true, async: false }) attachable;
@hasMany('device-event', { async: false }) events;
@hasMany('sensor', { async: false }) sensors;
@hasMany('custom-field-value', { async: false }) custom_field_values;
Expand Down
96 changes: 96 additions & 0 deletions addon/serializers/device.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ApplicationSerializer from '@fleetbase/ember-core/serializers/application';
import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';
import { isBlank } from '@ember/utils';

export default class DeviceSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {
/**
Expand All @@ -11,7 +12,102 @@ export default class DeviceSerializer extends ApplicationSerializer.extend(Embed
return {
telematic: { embedded: 'always' },
warranty: { embedded: 'always' },
attachable: { embedded: 'always' },
custom_field_values: { embedded: 'always' },
};
}

normalize(model, hash, prop) {
let attachableDomainType;

if (hash?.attachable) {
attachableDomainType = hash.attachable.type;
hash.attachable.type = this.attachableModelNameFromType(hash.attachable_type);
}

const normalized = super.normalize(model, hash, prop);

if (this.shouldRestoreAttachableDomainType(attachableDomainType)) {
this.restoreAttachableDomainType(normalized, attachableDomainType);
}

return normalized;
}

serializePolymorphicType(snapshot, json, relationship) {
let key = relationship.key;

if (key !== 'attachable') {
if (typeof super.serializePolymorphicType === 'function') {
return super.serializePolymorphicType(...arguments);
}
return;
}

const belongsTo = snapshot.belongsTo(key);
const isPolymorphicTypeBlank = isBlank(snapshot.attr(`${key}_type`));

if (!isPolymorphicTypeBlank) {
return;
}

key = this.keyForAttribute ? this.keyForAttribute(key, 'serialize') : key;

if (!belongsTo) {
json[`${key}_type`] = null;
return;
}

let type = belongsTo.modelName;

if (typeof type === 'string') {
type = type.replace(/^attachable-/, '');
}

json[`${key}_type`] = `fleet-ops:${type}`;
}

attachableModelNameFromType(type) {
if (!type || typeof type !== 'string') {
return undefined;
}

if (type.includes('\\')) {
type = type.split('\\').pop();
}

type = type
.replace(/^fleet-ops:/, '')
.replace(/^attachable-/, '')
.toLowerCase();

if (!['vehicle', 'asset'].includes(type)) {
return undefined;
}

return `attachable-${type}`;
}

shouldRestoreAttachableDomainType(type) {
if (!type || typeof type !== 'string') {
return false;
}

return !this.attachableModelNameFromType(type);
}

restoreAttachableDomainType(normalized, type) {
const attachable = normalized?.data?.relationships?.attachable?.data;

if (!attachable) {
return;
}

const included = normalized?.included?.find((resource) => resource.type === attachable.type && resource.id === attachable.id);

if (included) {
included.attributes = included.attributes ?? {};
included.attributes.type = type;
}
}
}
1 change: 1 addition & 0 deletions app/models/attachable-asset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@fleetbase/fleetops-data/models/attachable-asset';
1 change: 1 addition & 0 deletions app/models/attachable-vehicle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@fleetbase/fleetops-data/models/attachable-vehicle';
1 change: 1 addition & 0 deletions app/models/attachable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@fleetbase/fleetops-data/models/attachable';
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fleetbase/fleetops-data",
"version": "0.1.39",
"version": "0.1.40",
"description": "Fleetbase Fleet-Ops based models, serializers, transforms, adapters and GeoJson utility functions.",
"keywords": [
"fleetbase-data",
Expand Down
13 changes: 13 additions & 0 deletions tests/unit/models/attachable-asset-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { module, test } from 'qunit';
import { setupTest } from 'dummy/tests/helpers';

module('Unit | Model | attachable asset', function (hooks) {
setupTest(hooks);

test('it exists', function (assert) {
const store = this.owner.lookup('service:store');
const model = store.createRecord('attachable-asset', {});

assert.ok(model);
});
});
13 changes: 13 additions & 0 deletions tests/unit/models/attachable-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { module, test } from 'qunit';
import { setupTest } from 'dummy/tests/helpers';

module('Unit | Model | attachable', function (hooks) {
setupTest(hooks);

test('it exists', function (assert) {
const store = this.owner.lookup('service:store');
const model = store.createRecord('attachable', {});

assert.ok(model);
});
});
13 changes: 13 additions & 0 deletions tests/unit/models/attachable-vehicle-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { module, test } from 'qunit';
import { setupTest } from 'dummy/tests/helpers';

module('Unit | Model | attachable vehicle', function (hooks) {
setupTest(hooks);

test('it exists', function (assert) {
const store = this.owner.lookup('service:store');
const model = store.createRecord('attachable-vehicle', {});

assert.ok(model);
});
});
26 changes: 25 additions & 1 deletion tests/unit/models/device-event-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,34 @@ import { setupTest } from 'dummy/tests/helpers';
module('Unit | Model | device event', function (hooks) {
setupTest(hooks);

// Replace this with your real tests.
test('it exists', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('device-event', {});
assert.ok(model);
});

test('it exposes connectivity identity payload fields', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('device-event', {
device_id: 'BX-025',
device_imei: '867747078951793',
device_connection_status: 'offline',
telematic_uuid: 'telematic_1',
telematic_name: 'AFAQY',
provider_descriptor: {
label: 'AFAQY',
icon: '/engines-dist/images/telematics/providers/afaqy.webp',
},
});

assert.strictEqual(model.device_id, 'BX-025');
assert.strictEqual(model.device_imei, '867747078951793');
assert.strictEqual(model.device_connection_status, 'offline');
assert.strictEqual(model.telematic_uuid, 'telematic_1');
assert.strictEqual(model.telematic_name, 'AFAQY');
assert.deepEqual(model.provider_descriptor, {
label: 'AFAQY',
icon: '/engines-dist/images/telematics/providers/afaqy.webp',
});
});
});
11 changes: 10 additions & 1 deletion tests/unit/models/device-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@ import { setupTest } from 'dummy/tests/helpers';
module('Unit | Model | device', function (hooks) {
setupTest(hooks);

// Replace this with your real tests.
test('it exists', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('device', {});
assert.ok(model);
});

test('attachable is a synchronous polymorphic relationship', function (assert) {
const store = this.owner.lookup('service:store');
const relationship = store.modelFor('device').relationshipsByName.get('attachable');

assert.strictEqual(relationship.kind, 'belongsTo');
assert.strictEqual(relationship.type, 'attachable');
assert.true(relationship.options.polymorphic);
assert.false(relationship.options.async);
});
});
Loading
Loading