From d802eb111c5d990978deff7467919af8a50b6d82 Mon Sep 17 00:00:00 2001 From: Don Date: Fri, 22 Mar 2024 15:26:14 +1100 Subject: [PATCH] updates for transitions, db edits and refreshes in groups pages frontend/app/ Use injected .router for .transitionTo() instead of this in routes and this.target in controller, in : controllers/ group/edit.js : deleteGroup() and groups.js : selectedServerChanged() routes/group.js : beforeModel(). adapters/application.js : updateRecord() : use attributesToSave instead of snapshot._internalModel._relationshipProxyCache / changedAttributes. components/panel/manage-dataset.js : add : ownedByMe: alias(dataset.owner). datasetChangeGroup() : objectAttributeChanged(this.dataset, groupId), which pushes the change to dataset record Symbol attributesToSave. controllers/ group/edit.js and group.js : get services () is replaced by setting controller owner via setupController(), to enable setup of these values via @service. group.js : alias(model) group. group/edit.js : removeGroupMemberClient() and removeAllGroupMembers() : pass clientGroup object to removeGroupMember() instead of .id groups.js : serverObj() : fall back to primaryServer when server name is not given in queryParams. groups/add.js : addGroup() : pass client object instead of id as clientId. add group/index.js, with removeGroupMember() copied from controllers/groups.js with one change : get server from .model.server and derive apiServers from that, instead of using server = apiServers.lookupServerName(store.name). models/ client-group.js : clientId, groupId, client.js : clientGroups and group.js : clientGroups : specify the belongsTo / hasMany relation inverse field name, instead of null. routes/ group.js : add model(). beforeModel() : record .server instead of .store. group/edit.js and groups.js : setupController = setupControllerModelOwnerTarget serializers/ application.js : normalizeResponse() : add store.name to trace. groups-in.js : normalizeGroupsIn() and groups-in.js : normalizeGroupsOwn() : add param store, used instead of this.store. groups-own.js : normalize() : add debugger statement - seems not used. services/controls.js : apiServerSelectedOrPrimary() : access apiServers.lookupServerName without .get(). group/index.hbs : group-members param group change .group -> .model; pass action param removeGroupMember. utils/data/group.js : getGroups() : groupsPR : pass store to serializer .normalizeGroups{Own,In}() utils/ember-devel.js : add objectAttributeChanged(), setupControllerModelOwnerTarget(). # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Fri Mar 22 15:26:14 2024 +1100 # # On branch feature/upgradeFrontend # Your branch is ahead of 'origin/feature/upgradeFrontend' by 1 commit. # (use "git push" to publish your local commits) # # Changes to be committed: # modified: frontend/app/adapters/application.js # modified: frontend/app/components/panel/manage-dataset.js # modified: frontend/app/controllers/group.js # modified: frontend/app/controllers/group/edit.js # new file: frontend/app/controllers/group/index.js # modified: frontend/app/controllers/groups.js # modified: frontend/app/controllers/groups/add.js # modified: frontend/app/models/client-group.js # modified: frontend/app/models/client.js # modified: frontend/app/models/group.js # modified: frontend/app/routes/group.js # modified: frontend/app/routes/group/edit.js # modified: frontend/app/routes/groups.js # modified: frontend/app/serializers/application.js # modified: frontend/app/serializers/groups-in.js # modified: frontend/app/serializers/groups-own.js # modified: frontend/app/services/controls.js # deleted: frontend/app/services/store.js # modified: frontend/app/templates/group/index.hbs # modified: frontend/app/utils/data/group.js # modified: frontend/app/utils/ember-devel.js # # Changes not staged for commit: # modified: frontend/app/controllers/group/index.js # # Untracked files: # lb4app/client # lb4app/dnaSequence.0.fasta # lb4app/dnaSequence.1.fasta # lb4app/lb3app/common/index/ # lb4app/tmp/ # package-lock.json # --- frontend/app/adapters/application.js | 47 ++++++++++------- .../app/components/panel/manage-dataset.js | 9 +++- frontend/app/controllers/group.js | 3 +- frontend/app/controllers/group/edit.js | 33 ++++++------ frontend/app/controllers/group/index.js | 51 ++++++++++++++++++ frontend/app/controllers/groups.js | 21 +++++--- frontend/app/controllers/groups/add.js | 3 +- frontend/app/models/client-group.js | 4 +- frontend/app/models/client.js | 2 +- frontend/app/models/group.js | 2 +- frontend/app/routes/group.js | 22 ++++++-- frontend/app/routes/group/edit.js | 10 +++- frontend/app/routes/groups.js | 18 +++++-- frontend/app/serializers/application.js | 2 +- frontend/app/serializers/groups-in.js | 5 +- frontend/app/serializers/groups-own.js | 7 ++- frontend/app/services/controls.js | 4 +- frontend/app/services/store.js | 5 -- frontend/app/templates/group/index.hbs | 4 +- frontend/app/utils/data/group.js | 9 +++- frontend/app/utils/ember-devel.js | 52 +++++++++++++++++++ 21 files changed, 239 insertions(+), 74 deletions(-) create mode 100644 frontend/app/controllers/group/index.js delete mode 100644 frontend/app/services/store.js diff --git a/frontend/app/adapters/application.js b/frontend/app/adapters/application.js index 1cb959c9a..d4de20499 100644 --- a/frontend/app/adapters/application.js +++ b/frontend/app/adapters/application.js @@ -173,6 +173,7 @@ var config = { let data = {}; let object = store.peekRecord(snapshot.modelName, snapshot.id), + /* changedAttributes now contains relationships in some cases. */ changedAttributes = snapshot.changedAttributes(), changedAttributesKeys = Object.keys(changedAttributes), /** object.hasDirtyAttributes seems to be true if either an attribute or relationship has changed. @@ -181,24 +182,31 @@ var config = { * .groupId in the PATCH, to avoid writing [] to .blocks * This could be used more generally as other changes are added. */ - /** probably github.com/ef4/ember-data-relationship-tracker offers a better solution. */ - rc = snapshot._internalModel._relationshipProxyCache, - changedRelationshipKeys = Object.keys(rc); - if (! changedAttributesKeys.length && - (snapshot.modelName === 'dataset') && - (changedRelationshipKeys.length === 1) && - (changedRelationshipKeys[0] === "groupId" )) { - /* expect changedAttributes is {}. - * If needed to set .groupId and attributes in one save, this can be expanded. - */ - if (changedAttributesKeys.length) { - dLog(fnName, changedAttributes); + /** possibly github.com/ef4/ember-data-relationship-tracker offers a better solution, or : + * https://github.com/danielspaniel/ember-data-change-tracker + * https://www.npmjs.com/package/ember-data-relationship-dirty-tracking + * but they have not been updated to Ember v4. + */ + record = snapshot.record, + attributesToSave = record[Symbol.for('attributesToSave')]; + if (attributesToSave?.length) { + dLog(fnName, record.id, attributesToSave); + let attributeName; + while ((attributeName = attributesToSave.pop())) { + /** dataset record.get('clientId') is db id string instead of a proxy; + * to handle that case, could instead do : + * const value = record.get(idField), + * valueId = value.then ? value.get('id') : value + * data[attributeName] = valueId || null; + */ + const idField = attributeName.replace(/Id$/, 'Id.id'); + /* map undefined to null, for JSON web API. + * When dataset.groupId is set to null, rc.groupId.get('id') returns + * undefined; map this to null, which is valid JSON in PATCH. + */ + data[attributeName] = record.get(idField) || null; } - /* when dataset.groupId is set to null, rc.groupId.get('id') returns - * undefined; map this to null, which is valid JSON in PATCH. - * (snapshot._internalModel._record.groupId.content is null) - */ - data.groupId = rc.groupId.get('id') || null; + dLog(fnName, data, record.id, changedAttributes, attributesToSave); } else if ((changedAttributesKeys.length === 1) && (type.modelName !== 'feature')) { /* excluding 'feature' because having attributes .value and .values seems to * confuse snapshot.changedAttributes() - when .values.Ontology is set, @@ -215,8 +223,9 @@ var config = { let modelName = snapshot.modelName, serializer = store.serializerFor(modelName), - rename = serializer?.attrs[key]; - data[rename || key] = snapshot.__attributes[key]; + model = store.modelFor(modelName), + rename = serializer?._getMappedKey(key, model); // was serializer?.attrs[key] in Ember v3 + data[rename || key] = snapshot.attr(key); // was .__attributes[key]; } else { let serializer = store.serializerFor(type.modelName); diff --git a/frontend/app/components/panel/manage-dataset.js b/frontend/app/components/panel/manage-dataset.js index 50d696627..78ab409a5 100644 --- a/frontend/app/components/panel/manage-dataset.js +++ b/frontend/app/components/panel/manage-dataset.js @@ -5,7 +5,7 @@ import { later as run_later } from '@ember/runloop'; import $ from 'jquery'; -import { toPromiseProxy, toArrayPromiseProxy } from '../../utils/ember-devel'; +import { toPromiseProxy, toArrayPromiseProxy, objectAttributeChanged } from '../../utils/ember-devel'; import { getGroups } from '../../utils/data/group'; import { noGroup } from '../../utils/data/groups'; @@ -36,7 +36,11 @@ export default ManageBase.extend({ onEditable : function() { dLog('onEditable'); return false; }, - ownedByMe: alias("dataset.owner"), + /** alias("dataset.owner") does not appear to be updating, so instead using + * datasetOwned which is roughly equivalent, with the advantage that + * it uses sessionUserId whereas models/record.js : owner() uses .server.clientId + */ + ownedByMe: alias("datasetOwned"), apiHost : alias("dataset.store.name"), datasetMeta: computed("dataset._meta", function() { @@ -182,6 +186,7 @@ export default ManageBase.extend({ if (this.dataset.get('groupId.id') == group.get('id')) { dLog(fnName, 'no change', this.dataset.get('id'), group.get('id') ); } else { + objectAttributeChanged(this.dataset, 'groupId'); this.dataset.set('groupId', group === noGroup ? null : group); this.dataset.save() .catch((err) => this.set('datasetGroupErrMsg', 'Dataset Group change ' + group.id + ' not saved.\n' + err)); diff --git a/frontend/app/controllers/group.js b/frontend/app/controllers/group.js index 3f5c74109..ef270c3bb 100644 --- a/frontend/app/controllers/group.js +++ b/frontend/app/controllers/group.js @@ -2,6 +2,7 @@ import Controller from '@ember/controller'; import { computed, action } from '@ember/object'; import { alias } from '@ember/object/computed'; import { inject as service } from '@ember/service'; +import { getOwner } from '@ember/application'; // ----------------------------------------------------------------------------- @@ -18,7 +19,6 @@ export default class GroupController extends Controller { this.editingName = false; } - @action changeGroupName(group, newName) { const @@ -42,6 +42,7 @@ export default class GroupController extends Controller { } /** model is group */ + @alias('model') group; @alias('model.server') server; @action diff --git a/frontend/app/controllers/group/edit.js b/frontend/app/controllers/group/edit.js index 080c6304d..58f8318bb 100644 --- a/frontend/app/controllers/group/edit.js +++ b/frontend/app/controllers/group/edit.js @@ -2,7 +2,6 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; import { computed, action } from '@ember/object'; import { alias } from '@ember/object/computed'; -import { getOwner } from '@ember/application'; /* global Ember */ @@ -25,15 +24,11 @@ const allowGroupsWhenPrivate = false; * @param model group */ export default class GroupEditController extends Controller { - // @service auth; - - // @service apiServers; - // apiServers: service(), - - // @service session; - // session : service(), - - // @service controls; + @service auth; + @service apiServers; + @service session; + @service controls; + @service router; // newClientName : string; constructor() { @@ -41,17 +36,18 @@ export default class GroupEditController extends Controller { this.newClientName = undefined; } - /** lookup owner and services when required. */ + /** lookup owner and services when required. @computed() get services () { - let owner = getOwner(this.target); + let owner = getOwner(this.target || this.model || this.group); let apiServers = owner.lookup('service:apiServers'), session = owner.lookup('service:session'), auth = owner.lookup('service:auth'), controls = owner.lookup('service:controls'), + router = owner.lookup('service:router'), services = { - apiServers, session, auth, controls + apiServers, session, auth, controls, router, }; return services; } @@ -59,6 +55,8 @@ export default class GroupEditController extends Controller { @alias('services.session') session; @alias('services.auth') auth; @alias('services.controls') controls; + @alias('services.router') router; + */ @alias('model') group; /** .server.store === .groupStore. */ @@ -78,6 +76,9 @@ export default class GroupEditController extends Controller { @action refresh() { + if (! this.target) { + dLog('refresh', this.model); + } this.send('refreshModel'); } @@ -196,7 +197,7 @@ export default class GroupEditController extends Controller { fnName = 'removeGroupMemberClient', clientGroup = this.clientToClientGroup(client); if (clientGroup) { - this.removeGroupMember(clientGroup.id); + this.removeGroupMember(clientGroup); } } @@ -231,7 +232,7 @@ export default class GroupEditController extends Controller { group = this.model, cgs = group.clientGroups, destroyPs = cgs.map((cg) => { - return this.removeGroupMember(cg.id); + return this.removeGroupMember(cg); }); dLog(fnName, cgs); return Promise.all(destroyPs); @@ -279,7 +280,7 @@ export default class GroupEditController extends Controller { routeGroups = owner.lookup('route:groups'); routeGroups.transitionTo('groups'); */ - this.target.transitionTo('groups'); + this.router.transitionTo('groups'); }) .catch((error) => { const errorDetail = error?.errors[0]; diff --git a/frontend/app/controllers/group/index.js b/frontend/app/controllers/group/index.js new file mode 100644 index 000000000..a53b8c563 --- /dev/null +++ b/frontend/app/controllers/group/index.js @@ -0,0 +1,51 @@ +import Controller from '@ember/controller'; +import { getOwner } from '@ember/application'; +import { computed, action } from '@ember/object'; + +import { removeGroupMember } from '../../utils/data/group'; + +// ----------------------------------------------------------------------------- + +const dLog = console.debug; + +// ----------------------------------------------------------------------------- + + +/** + * @param model group + */ +export default class GroupIndexController extends Controller { + + //---------------------------------------------------------------------------- + + // copied from controllers/groups.js + /** + * @param clientGroup is from this.model.groupsIn, via #each in .hbs + */ + @action + removeGroupMember(clientGroup) { + const + fnName = 'removeGroupMember', + msgName = fnName + 'Msg', + /** model is group */ + server = this.model.server, + apiServers = server.apiServers, + clientGroupId = clientGroup.id; + + this.set(msgName, ''); + let + destroyP = removeGroupMember(apiServers, server, clientGroup, clientGroupId); + destroyP + .then((cg) => { + this.set('selectedClientGroupId', null); + this.send('refreshModel'); + }) + .catch((errorText) => { + this.set(msgName, errorText); + }); + return destroyP; + }; + + //---------------------------------------------------------------------------- + +} diff --git a/frontend/app/controllers/groups.js b/frontend/app/controllers/groups.js index 70b6edca9..25857e9a2 100644 --- a/frontend/app/controllers/groups.js +++ b/frontend/app/controllers/groups.js @@ -1,4 +1,5 @@ import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; import { computed, action } from '@ember/object'; import { alias } from '@ember/object/computed'; import { getOwner } from '@ember/application'; @@ -12,15 +13,18 @@ const dLog = console.debug; // ----------------------------------------------------------------------------- export default class GroupsController extends Controller { - - // @service apiServers; + @service router; + @service apiServers; queryParams = ['server']; // .server is initially undefined. @computed('server') get serverObj() { - const server = this.apiServers.lookupServerTabId(this.server); + const + apiServers = this.apiServers, + server = this.server ? apiServers.lookupServerTabId(this.server) : + apiServers.primaryServer; return server; } @@ -29,28 +33,33 @@ export default class GroupsController extends Controller { this.newClientName = undefined; } - /** lookup owner and services when required. */ + /** lookup owner and services when required. @computed() get services () { let apiServers; + let router; if (this.target) { + dLog('services', 'target route', this.target); let owner = getOwner(this.target); apiServers = owner.lookup('service:apiServers'); + router = owner.lookup('service:router'); } else if (this.model.server) { apiServers = this.model.server.apiServers; } const services = { - apiServers + apiServers, router, }; return services; } @alias('services.apiServers') apiServers; + @alias('services.router') router; + */ @action selectedServerChanged(server) { dLog('selectedServerChanged', server); let queryParams = {server : server.tabId}; - this.target.transitionTo({queryParams}); + this.router.transitionTo({queryParams}); // this.send('refreshModel'); } diff --git a/frontend/app/controllers/groups/add.js b/frontend/app/controllers/groups/add.js index 0269e1c32..6ae7c1498 100644 --- a/frontend/app/controllers/groups/add.js +++ b/frontend/app/controllers/groups/add.js @@ -46,12 +46,13 @@ export default Controller.extend({ dLog(fnName, this.clientIdSession); { let store = this.get('apiServers.primaryServer.store'); + const client = store.peekRecord('client', clientId); dLog(fnName, this.store, store); { var group = store.createRecord('group', { name: newGroupName, - clientId + clientId : client }); let p = group.save(); diff --git a/frontend/app/models/client-group.js b/frontend/app/models/client-group.js index 5ba31f833..d132dca53 100644 --- a/frontend/app/models/client-group.js +++ b/frontend/app/models/client-group.js @@ -2,8 +2,8 @@ import Model, { attr, belongsTo } from '@ember-data/model'; export default class ClientGroupModel extends Model { - @belongsTo('client', { async: true, inverse : null/*'clientGroup'*/ }) clientId; - @belongsTo('group', { async: true, inverse : null/*'clientGroups'*/ }) groupId; + @belongsTo('client', { async: true, inverse : 'clientGroups' }) clientId; + @belongsTo('group', { async: true, inverse : 'clientGroups' }) groupId; @attr('boolean') isVisible; diff --git a/frontend/app/models/client.js b/frontend/app/models/client.js index beba6dd25..60d44f758 100644 --- a/frontend/app/models/client.js +++ b/frontend/app/models/client.js @@ -6,6 +6,6 @@ export default class ClientModel extends Model { @hasMany('group', {async: true, inverse : 'clientId'}) groupsOwn; @hasMany('group', {async: true, inverse : 'clients'}) groups; - @hasMany('client-group', {async: true, inverse : null/*'client'*/}) clientGroups; + @hasMany('client-group', {async: true, inverse : 'clientId'}) clientGroups; } diff --git a/frontend/app/models/group.js b/frontend/app/models/group.js index cbc12d154..194ea35fc 100644 --- a/frontend/app/models/group.js +++ b/frontend/app/models/group.js @@ -30,7 +30,7 @@ export default Model.extend({ clientId : belongsTo('client', {async: true, inverse: null/*'groupsOwn'*/}), /** members of the group */ clients: hasMany('client', {async: false, inverse: null/*'groups'*/}), - clientGroups: hasMany('client-group', { async: false, inverse: null/*'group'*/ }), + clientGroups: hasMany('client-group', { async: false, inverse: 'groupId'}), // --------------------------------------------------------------------------- diff --git a/frontend/app/routes/group.js b/frontend/app/routes/group.js index 499109fb7..0ea1ce646 100644 --- a/frontend/app/routes/group.js +++ b/frontend/app/routes/group.js @@ -8,26 +8,38 @@ import { toArrayPromiseProxy } from '../utils/ember-devel'; const dLog = console.debug; export default class GroupRoute extends Route { + @service router; @service session; + @service apiServers; + activate() { this.controllerFor('group').send('reset'); } + model(params) { + const + server = this.server || this.apiServers.primaryServer, + store = server.store || this.store; + dLog('model', server?.name, store.name); + const groupP = store.findRecord('group', params.group_id); + return groupP; + } + beforeModel(transition) { const fnName = 'beforeModel'; // pre-Octane equivalent using AuthenticatedRouteMixin : authenticationRoute = 'login'; // this.session.requireAuthentication(transition, 'login'); if (! this.session.isAuthenticated) { dLog(fnName, '!isAuthenticated', this.session); - this.transitionTo('login'); + this.router.transitionTo('login'); } /** can add server to queryParams, as in routes/groups */ - let store = transition.from?.attributes.server?.store; - if (store) { - dLog(fnName, this.store === store, store?.name); - this.store = store; + let server = transition.from?.attributes.server; + if (server) { + dLog(fnName, server.name, server.store?.name); + this.server = server; } } diff --git a/frontend/app/routes/group/edit.js b/frontend/app/routes/group/edit.js index 544a5bc03..de1b9f622 100644 --- a/frontend/app/routes/group/edit.js +++ b/frontend/app/routes/group/edit.js @@ -2,7 +2,7 @@ import Route from '@ember/routing/route'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; - +import { setupControllerModelOwnerTarget } from '../../utils/ember-devel'; import { getGroups, groupDatasets } from '../../utils/data/group'; // ----------------------------------------------------------------------------- @@ -16,6 +16,12 @@ export default class GroupEditRoute extends Route { @service auth; @service apiServers; + //---------------------------------------------------------------------------- + + setupController = setupControllerModelOwnerTarget; + + //---------------------------------------------------------------------------- + @action willTransition(transition) { dLog('willTransition', transition); @@ -43,7 +49,7 @@ export default class GroupEditRoute extends Route { */ @action refreshModel() { - const fnName = 'refreshModel'; + const fnName = 'routes/group/edit:refreshModel'; dLog(fnName); let group = this.controller.model, diff --git a/frontend/app/routes/groups.js b/frontend/app/routes/groups.js index 6731c2adf..7ae3347f4 100644 --- a/frontend/app/routes/groups.js +++ b/frontend/app/routes/groups.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { alias } from '@ember/object/computed'; +import { setupControllerModelOwnerTarget } from '../utils/ember-devel'; const dLog = console.debug; @@ -18,6 +19,12 @@ export default class GroupsRoute extends Route { @alias('controls.apiServerSelectedOrPrimary') defaultServer; + //---------------------------------------------------------------------------- + + setupController = setupControllerModelOwnerTarget; + + //---------------------------------------------------------------------------- + beforeModel() { const fnName = 'beforeModel'; if (! this.session.isAuthenticated) { @@ -28,15 +35,16 @@ export default class GroupsRoute extends Route { /** Model contains the groups of the selected server. */ model(queryParams, transition) { + const fnName = 'routes/groups:model'; let server; /** queryParams.server is text id : server.tabId */ if (queryParams.server) { server = this.apiServers.lookupServerTabId(queryParams.server); // this.controller ? this.controller.get('serverObj') : if (this.controller && (queryParams.server !== this.controller.get('server'))) { - dLog('model', this.controller.server, '!==', queryParams.server); + dLog(fnName, this.controller.server, '!==', queryParams.server); } - dLog('model', queryParams.server, server); + dLog(fnName, queryParams.server, server); } /* queryParams.server may be invalid, e.g. refresh tab while secondary * server is selected in groups; .apiServers.servers contains only the @@ -62,13 +70,15 @@ export default class GroupsRoute extends Route { groupsP = fieldNames.map((fieldName) => [fieldName, groups.get(fieldName)]), modelP = Object.fromEntries(groupsP); modelP.server = server; - modelP.groupsOwn.then((gs) => console.log('routes/groups:model() groupsOwn.then', gs.map((g) => [g.get('id'), g.get('name')]))); - + modelP.groupsOwn.then((gs) => console.log(fnName, 'groupsOwn.then', gs.map((g) => [g.get('id'), g.get('name')]))); + modelP.groupsIn.then((cgs) => console.log(fnName, 'groupsIn.then', cgs.map((cg) => + [cg.clientId.id, cg.groupId.id, cg.isVisible, cg.isDestroying]))); return modelP; } @action refreshModel() { + dLog('routes/groups:refreshModel'); /** called before model(), so this.server is the previous value, when changing servers. * groups.refresh() may not be required when simply switching between servers (via select-server) * let server = this.get('controller.model.server') || this.get('controller.serverObj') || this.server; diff --git a/frontend/app/serializers/application.js b/frontend/app/serializers/application.js index 3f454cbfd..460b87787 100644 --- a/frontend/app/serializers/application.js +++ b/frontend/app/serializers/application.js @@ -53,7 +53,7 @@ export default RESTSerializer.extend(/*PartialModelRESTSerializer,*/ { }) }, normalizeResponse(store, primaryModelClass, payload, id, requestType) { - dLog('application normalizeResponse', primaryModelClass.modelName, payload, id, requestType); + dLog('application normalizeResponse', store.name, primaryModelClass.modelName, payload, id, requestType); let payloadTemp = {}; payloadTemp[primaryModelClass.modelName] = payload return this._super(store, primaryModelClass, payloadTemp, id, requestType); diff --git a/frontend/app/serializers/groups-in.js b/frontend/app/serializers/groups-in.js index 831275e03..da4edbfb2 100644 --- a/frontend/app/serializers/groups-in.js +++ b/frontend/app/serializers/groups-in.js @@ -18,9 +18,10 @@ export default class ClientGroupSerializer extends ApplicationSerializer { return ret; }; /** normalize the result of /groups/in + * @param store * @param d response data */ - normalizeGroupsIn(d) { + normalizeGroupsIn(store, d) { const fnName = 'normalizeGroupsIn'; dLog(fnName, d); let @@ -29,7 +30,7 @@ export default class ClientGroupSerializer extends ApplicationSerializer { /** client is handled by attribute2relationship() following */ modelNameIncluded = ['group'], includedPlural = false; - let result = normalizeDataEmbedded(this.store, modelName, modelNameIncluded, includedPlural, d); + let result = normalizeDataEmbedded(store, modelName, modelNameIncluded, includedPlural, d); /** If the group has been deleted and not the client-group, then included will be []. */ let data = result.included[0]; diff --git a/frontend/app/serializers/groups-own.js b/frontend/app/serializers/groups-own.js index c6bf83c01..d78e368cd 100644 --- a/frontend/app/serializers/groups-own.js +++ b/frontend/app/serializers/groups-own.js @@ -21,6 +21,8 @@ export default class GroupsOwnSerializer extends ApplicationSerializer { let payload = {}; payload[model.modelName] = hash; // model === this.serializerFor(prop) + dLog('normalize', this.store.name, this._super); + debugger; ret = this._super(this.store, model, payload, hash.id, /*requestType*/'findRecord'); } else { ret = this._super(...arguments); @@ -29,9 +31,10 @@ export default class GroupsOwnSerializer extends ApplicationSerializer { }; /** normalize the result of /groups/in + * @param store * @param d response data */ - normalizeGroupsOwn(d) { + normalizeGroupsOwn(store, d) { const fnName = 'normalizeGroupsOwn'; dLog(fnName, d); let @@ -39,7 +42,7 @@ export default class GroupsOwnSerializer extends ApplicationSerializer { modelName = 'group', modelNameIncluded = 'client', includedPlural = true; - let result = normalizeDataEmbedded(this.store, modelName, [modelNameIncluded], includedPlural, d); + let result = normalizeDataEmbedded(store, modelName, [modelNameIncluded], includedPlural, d); let data = result.data; // data.relationships is initialised diff --git a/frontend/app/services/controls.js b/frontend/app/services/controls.js index d51ad71a5..318fb4966 100644 --- a/frontend/app/services/controls.js +++ b/frontend/app/services/controls.js @@ -34,7 +34,9 @@ export default Service.extend(Evented, { // factored from components/goto-feature-list.js:blocksUnique() . (taskGet getBlocksOfFeatures) let serverTabSelectedName = this.get('serverTabSelected'), - serverTabSelected = serverTabSelectedName && this.get('apiServers').lookupServerName(serverTabSelectedName), + // init() does addServer() of primary, so evaluate this even if ! serverTabSelectedName + apiServers = this.get('apiServers'), + serverTabSelected = serverTabSelectedName && apiServers.lookupServerName(serverTabSelectedName), apiServer = serverTabSelected || this.get('apiServers.primaryServer'); return apiServer; }), diff --git a/frontend/app/services/store.js b/frontend/app/services/store.js deleted file mode 100644 index 5ceae335c..000000000 --- a/frontend/app/services/store.js +++ /dev/null @@ -1,5 +0,0 @@ -// import Service from '@ember/service'; - -// export default class StoreService extends Service {} - -export { default } from 'ember-data/store'; diff --git a/frontend/app/templates/group/index.hbs b/frontend/app/templates/group/index.hbs index 487f07753..0e7fe50a4 100644 --- a/frontend/app/templates/group/index.hbs +++ b/frontend/app/templates/group/index.hbs @@ -1,6 +1,8 @@ - {{group/group-members group=this.group selectMember=(noop)}} + {{group/group-members group=this.model + selectMember=(noop) + removeGroupMember=(action this.removeGroupMember) }} {{outlet}} \ No newline at end of file diff --git a/frontend/app/utils/data/group.js b/frontend/app/utils/data/group.js index ffaacdd6a..aed28cfb9 100644 --- a/frontend/app/utils/data/group.js +++ b/frontend/app/utils/data/group.js @@ -23,6 +23,7 @@ function getGroups(auth, own, server, apiServers) { store = server.store, apiName = own ? 'own' : 'in', config = groupApi[apiName], + /** result serializer.store is default store, !== store param */ serializer = store.serializerFor(config.primaryModelName), /** promise yielding an array of records */ groupsPR = own ? getGroups2(auth, own, server, apiServers) : @@ -32,7 +33,7 @@ function getGroups(auth, own, server, apiServers) { * in : client-group, own : group */ let cgrs = cgs.map((cg) => { - let j = serializer[config.normalizerName](cg), + let j = serializer[config.normalizerName](store, cg), jr = /*serializer.*/store.push(j); return jr; }); @@ -85,7 +86,11 @@ function clientGroupsToGroups(cgs) { /** * @return a promise, yielding the record of the deleted ClientGroup, or * throwing a text error message derived from the API error. - * @param clientGroup, clientGroupId. + * @param apiServers + * @param server api-server + * @param clientGroup Ember Data Object + * @param clientGroupId DB Id + * @desc * Originally clientGroup was found from clientGroupId in controllers/group/edit.js; * Now, in both uses : clientGroupId is now clientGroup.id */ diff --git a/frontend/app/utils/ember-devel.js b/frontend/app/utils/ember-devel.js index f98d38908..24134a0ca 100644 --- a/frontend/app/utils/ember-devel.js +++ b/frontend/app/utils/ember-devel.js @@ -1,4 +1,5 @@ import { later as run_later } from '@ember/runloop'; +import { getOwner, setOwner } from '@ember/application'; /*----------------------------------------------------------------------------*/ /* Various utility functions for development / debugging of Ember objects. */ @@ -202,6 +203,57 @@ function compareDependencies(object, label, dependencies) { objectDependenciesCache.set(object, current); } +//------------------------------------------------------------------------------ + +export { objectAttributeChanged }; +/** Record the name of an ember data object field attribute which has changed + * and should be included in subsequent PATCH to server REST API. + * + * There are some modules which could support this, but they don't appear to + * have been updated to Ember v4 : + * https://github.com/ef4/ember-data-relationship-tracker + * https://www.npmjs.com/package/ember-data-relationship-tracker + * https://www.npmjs.com/package/ember-data-relationship-dirty-tracking + * https://github.com/danielspaniel/ember-data-change-tracker + * https://github.com/jpoiri/ember-dirtier + */ +function objectAttributeChanged(object, attributeName) { + const + key = Symbol.for('attributesToSave'), + array = object[key] || (object[key] = []); + array.push(attributeName); +} + +//------------------------------------------------------------------------------ + +export { setupControllerModelOwnerTarget }; +/** Used by routes to set up controller.model, controller.target, and owner of this. + * @param controller + * @param model + * @param this route + * Usage : in class GroupsRoute extends Route { : setupController = setupController; + */ +function setupControllerModelOwnerTarget(controller, model) { + this._super(controller, model); + const fnName = 'routes/' + this.fullRouteName + ':setupController'; + dLog(fnName, 'model', model); + if (controller.model !== model) { + dLog(fnName, 'set model', model); + controller.set('model', model); + } + if (! getOwner(controller)) { + const container = getOwner(this); + setOwner(controller, container); + dLog(fnName, 'set controller owner', container, controller, this); + } + // this seems required atm - not clear why + if (! controller.target) { + dLog(fnName, 'set controller.target', this, this.routeName, controller, controller._debugContainerKey); + controller.target = this; + } +} + + // ----------------------------------------------------------------------------- /** Show id and type attributes of obj.