Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFR] Refactor referenced data #444

Merged
merged 6 commits into from
May 19, 2015
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
60 changes: 38 additions & 22 deletions src/javascripts/ng-admin/Crud/routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,16 @@ define(function (require) {
totalItems: ['response', function (response) {
return response.totalItems;
}],
referencedData: ['ReadQueries', 'view', 'response', function (ReadQueries, view, response) {
return ReadQueries.getReferencedData(view.getReferences(), response.data);
nonOptimizedReferencedData: ['ReadQueries', 'view', 'response', function (ReadQueries, view, response) {
return ReadQueries.getFilteredReferenceData(view.getNonOptimizedReferences(), response.data);
}],
referencedEntries: ['dataStore', 'view', 'referencedData', function (dataStore, view, referencedData) {
var references = view.getReferences();
var referencedEntries;
optimizedReferencedData: ['ReadQueries', 'view', 'response', function (ReadQueries, view, response) {
return ReadQueries.getOptimizedReferencedData(view.getOptimizedReferences(), response.data);
}],
referencedEntries: ['dataStore', 'view', 'nonOptimizedReferencedData', 'optimizedReferencedData', function (dataStore, view, nonOptimizedReferencedData, optimizedReferencedData) {
var references = view.getReferences(),
referencedData = angular.extend(nonOptimizedReferencedData, optimizedReferencedData),
referencedEntries;

for (var name in referencedData) {
referencedEntries = dataStore.mapEntries(
Expand Down Expand Up @@ -118,7 +122,7 @@ define(function (require) {
return true;
}],
filterData: ['ReadQueries', 'view', function (ReadQueries, view) {
return ReadQueries.getReferencedData(view.getFilterReferences());
return ReadQueries.getAllReferencedData(view.getFilterReferences());
}],
filterEntries: ['dataStore', 'view', 'filterData', function (dataStore, view, filterData) {
var filters = view.getFilterReferences();
Expand Down Expand Up @@ -170,12 +174,16 @@ define(function (require) {
rawEntry
);
}],
referencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) {
return ReadQueries.getReferencedData(view.getReferences(), [entry.values]);
nonOptimizedReferencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) {
return ReadQueries.getFilteredReferenceData(view.getNonOptimizedReferences(), [entry.values]);
}],
optimizedReferencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) {
return ReadQueries.getOptimizedReferencedData(view.getOptimizedReferences(), [entry.values]);
}],
referencedEntries: ['dataStore', 'view', 'referencedData', function (dataStore, view, referencedData) {
var references = view.getReferences();
var referencedEntries;
referencedEntries: ['dataStore', 'view', 'nonOptimizedReferencedData', 'optimizedReferencedData', function (dataStore, view, nonOptimizedReferencedData, optimizedReferencedData) {
var references = view.getReferences(),
referencedData = angular.extend(nonOptimizedReferencedData, optimizedReferencedData),
referencedEntries;

for (var name in referencedData) {
referencedEntries = dataStore.mapEntries(
Expand Down Expand Up @@ -247,12 +255,16 @@ define(function (require) {

return entry;
}],
referencedData: ['ReadQueries', 'view', function (ReadQueries, view) {
return ReadQueries.getReferencedData(view.getReferences());
nonOptimizedReferencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) {
return ReadQueries.getFilteredReferenceData(view.getNonOptimizedReferences(), [entry.values]);
}],
referencedEntries: ['dataStore', 'view', 'referencedData', function (dataStore, view, referencedData) {
var references = view.getReferences();
var referencedEntries;
optimizedReferencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) {
return ReadQueries.getOptimizedReferencedData(view.getOptimizedReferences(), [entry.values]);
}],
referencedEntries: ['dataStore', 'view', 'nonOptimizedReferencedData', 'optimizedReferencedData', function (dataStore, view, nonOptimizedReferencedData, optimizedReferencedData) {
var references = view.getReferences(),
referencedData = angular.extend(nonOptimizedReferencedData, optimizedReferencedData),
referencedEntries;

for (var name in referencedData) {
referencedEntries = dataStore.mapEntries(
Expand Down Expand Up @@ -300,13 +312,17 @@ define(function (require) {
rawEntry
);
}],
referencedData: ['ReadQueries', 'view', function (ReadQueries, view) {
return ReadQueries.getReferencedData(view.getReferences());
nonOptimizedReferencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) {
return ReadQueries.getFilteredReferenceData(view.getNonOptimizedReferences(), [entry.values]);
}],
optimizedReferencedData: ['ReadQueries', 'view', 'entry', function (ReadQueries, view, entry) {
return ReadQueries.getOptimizedReferencedData(view.getOptimizedReferences(), [entry.values]);
}],
referencedEntries: ['dataStore', 'view', 'referencedData', function (dataStore, view, referencedData) {
var references = view.getReferences();
referencedEntries: ['dataStore', 'view', 'nonOptimizedReferencedData', 'optimizedReferencedData', function (dataStore, view, nonOptimizedReferencedData, optimizedReferencedData) {
var references = view.getReferences(),
referencedData = angular.extend(nonOptimizedReferencedData, optimizedReferencedData),
referencedEntries;

var referencedEntries;
for (var name in referencedData) {
referencedEntries = dataStore.mapEntries(
references[name].targetEntity().name(),
Expand Down Expand Up @@ -388,7 +404,7 @@ define(function (require) {
templateProvider: templateProvider('BatchDeleteView', batchDeleteTemplate),
params: {
entity: null,
ids: [],
ids: []
},
resolve: {
view: viewProvider('BatchDeleteView'),
Expand Down
161 changes: 120 additions & 41 deletions src/javascripts/ng-admin/es6/lib/Queries/ReadQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ class ReadQueries extends Queries {
page = page || 1;
let url = view.getUrl();

return this.getRawValues(view.entity, view.name(), view.type, page, view.perPage(), filters, view.filters(), sortField || view.getSortFieldName(), sortDir || view.sortDir(), url)
sortField = sortField || view.getSortFieldName();
sortDir = sortDir || view.sortDir();

return this.getRawValues(view.entity, view.name(), view.type, page, view.perPage(), filters, view.filters(), sortField, sortDir, url)
.then((values) => {
return {
data: values.data,
Expand Down Expand Up @@ -63,16 +66,19 @@ class ReadQueries extends Queries {
getRawValues(entity, viewName, viewType, page, perPage, filterValues, filterFields, sortField, sortDir, url) {
let params = {};

// Compute pagination
if (page !== -1) {
params._page = (typeof (page) === 'undefined') ? 1 : parseInt(page, 10);
params._perPage = perPage;
}

// Compute sorting
if (sortField && sortField.split('.')[0] === viewName) {
params._sortField = sortField.split('.')[1];
params._sortDir = sortDir;
}

// Compute filtering
if (filterValues && Object.keys(filterValues).length !== 0) {
params._filters = {};
let filterName;
Expand All @@ -96,75 +102,148 @@ class ReadQueries extends Queries {

/**
* Returns all References for an entity with associated values [{targetEntity.identifier: targetLabel}, ...]
* by calling the API for each entries
*
* @param {Object} references A hash of Reference and ReferenceMany objects
* @param {ReferenceField} references A hash of Reference and ReferenceMany objects
* @param {Array} rawValues
*
* @returns {promise}
* @returns {Promise}
*/
getReferencedData(references, rawValues) {
let getRawValues = this.getRawValues.bind(this),
getOne = this.getOne.bind(this),
identifiers,
calls = [],
data;
getFilteredReferenceData(references, rawValues) {
if (!references || !Object.keys(references).length) {
return this._promisesResolver.empty({});
}

let getOne = this.getOne.bind(this),
calls = [];

for (let i in references) {
let reference = references[i],
targetEntity = reference.targetEntity();

if (!rawValues) {
calls.push(getRawValues(targetEntity, targetEntity.name() + '_ListView', 'listView', 1, reference.perPage(), reference.filters(), {}, reference.sortField(), reference.sortDir()));
targetEntity = reference.targetEntity(),
identifiers = reference.getIdentifierValues(rawValues);

continue;
for (let k in identifiers) {
calls.push(getOne(targetEntity, 'listView', identifiers[k], reference.name()));
}
}

// get only for identifiers
identifiers = reference.getIdentifierValues(rawValues);
return this.fillFilteredReferencedData(calls, references, rawValues);
};

/**
* Returns all References for an entity with associated values [{targetEntity.identifier: targetLabel}, ...]
* by calling the API once
*
* @param {[ReferenceField]} references A hash of Reference and ReferenceMany objects
* @param {Array} rawValues
*
* @returns {Promise}
*/
getOptimizedReferencedData(references, rawValues) {
if (!references || !Object.keys(references).length) {
return this._promisesResolver.empty({});
}

let getRawValues = this.getRawValues.bind(this),
calls = [];

for (let i in references) {
let reference = references[i],
targetEntity = reference.targetEntity(),
identifiers = reference.getIdentifierValues(rawValues);

// Check if we should retrieve values with 1 or multiple requests
if (reference.hasSingleApiCall()) {
let singleCallFilters = reference.getSingleApiCall(identifiers);
calls.push(getRawValues(targetEntity, targetEntity.name() + '_ListView', 'listView', 1, reference.perPage(), singleCallFilters, {}, reference.sortField(), reference.sortDir()));
let singleCallFilters = reference.getSingleApiCall(identifiers);
calls.push(getRawValues(targetEntity, targetEntity.name() + '_ListView', 'listView', 1, reference.perPage(), singleCallFilters, {}, reference.sortField(), reference.sortDir()));
}

continue;
}
return this.fillOptimizedReferencedData(calls, references);
}

for (let k in identifiers) {
calls.push(getOne(targetEntity, 'listView', identifiers[k], reference.name()));
}
/**
* Returns all References for an entity with associated values [{targetEntity.identifier: targetLabel}, ...]
* without filters on an entity
*
* @param {[ReferenceField]} references A hash of Reference and ReferenceMany objects
*
* @returns {Promise}
*/
getAllReferencedData(references) {
if (!references || !Object.keys(references).length) {
return this._promisesResolver.empty({});
}

// Fill all reference entries
return this._promisesResolver.allEvenFailed(calls)
let calls = [],
getRawValues = this.getRawValues.bind(this);

for (let i in references) {
let reference = references[i],
targetEntity = reference.targetEntity();

calls.push(getRawValues(targetEntity, targetEntity.name() + '_ListView', 'listView', 1, reference.perPage(), reference.filters(), {}, reference.sortField(), reference.sortDir()));
}

return this.fillOptimizedReferencedData(calls, references);
}

/**
* Fill all reference entries to return [{targetEntity.identifier: targetLabel}, ...]
*
* @param {[Promise]} apiCalls
* @param {[Reference]} references
* @returns {Promise}
*/
fillOptimizedReferencedData(apiCalls, references) {
return this._promisesResolver.allEvenFailed(apiCalls)
.then((responses) => {
if (responses.length === 0) {
return {};
}

let referencedData = {},
response,
i = 0;

for (let j in references) {
let reference = references[j],
singleCallFilters = reference.getSingleApiCall(identifiers);

// Retrieve entries depending on 1 or many request was done
if (singleCallFilters || !rawValues) {
response = responses[i++];
if (response.status == 'error') {
// the response failed
continue;
}

referencedData[reference.name()] = response.result.data;

// Retrieve entries depending on 1 or many request was done
if (response.status == 'error') {
// the response failed
continue;
}

data = [];
identifiers = reference.getIdentifierValues(rawValues);
referencedData[reference.name()] = response.result.data;
}

return referencedData;
});
}

/**
* Fill all reference entries to return [{targetEntity.identifier: targetLabel}, ...]
*
* @param {[Promise]} apiCalls
* @param {[Reference]} references
* @param {[Object]} rawValues
* @returns {Promise}
*/
fillFilteredReferencedData(apiCalls, references, rawValues) {
return this._promisesResolver.allEvenFailed(apiCalls)
.then((responses) => {
if (responses.length === 0) {
return {};
}

let referencedData = {},
response,
i = 0;

for (let j in references) {
let data = [],
reference = references[j],
identifiers = reference.getIdentifierValues(rawValues);

for (let k in identifiers) {
response = responses[i++];
if (response.status == 'error') {
Expand All @@ -183,7 +262,7 @@ class ReadQueries extends Queries {

return referencedData;
});
};
}

/**
* Returns all ReferencedList for an entity for associated values [{targetEntity.identifier: [targetFields, ...]}}
Expand Down Expand Up @@ -217,7 +296,7 @@ class ReadQueries extends Queries {
for (let i in referencedLists) {
let response = responses[j++];
if (response.status == 'error') {
// one of the responses failed
// If a response fail, skip it
continue;
}

Expand Down
6 changes: 6 additions & 0 deletions src/javascripts/ng-admin/es6/lib/Utils/PromisesResolver.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@

class PromisesResolver {
static empty(value) {
return new Promise((resolve) => {
resolve(value);
});
}

static allEvenFailed(promises) {
if (!Array.isArray(promises)) {
throw Error('allEvenFailed can only handle an array of promises');
Expand Down
29 changes: 29 additions & 0 deletions src/javascripts/ng-admin/es6/lib/View/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ class View {
return result;
}

getNonOptimizedReferences() {
return this._getReferencesByOptimizationType(false);
}

getOptimizedReferences() {
return this._getReferencesByOptimizationType(true);
}

getReferencedLists() {
let result = {};
let lists = this._fields.filter(f => f.type() === 'referenced_list');
Expand Down Expand Up @@ -231,6 +239,27 @@ class View {
}
});
}

/**
*
* @param {Boolean} optimized
* @returns {[Reference]}
* @private
*/
_getReferencesByOptimizationType(optimized=true) {
let result = {},
references = this.getReferences();

for (let i in references) {
let reference = references[i];

if (!!reference.getSingleApiCall() === optimized) {
result[i] = reference;
}
}

return result;
}
}

export default View;
Loading