mirror of
https://github.com/datahub-project/datahub.git
synced 2025-08-22 08:08:01 +00:00
Merge pull request #704 from theseyi/ownership
ts all the things. refactor getuserentities: cache and allow only one…
This commit is contained in:
commit
e20a5c84cb
@ -174,6 +174,20 @@ export default Component.extend({
|
|||||||
updatedOwner,
|
updatedOwner,
|
||||||
...updatingOwners.slice(ownerPosition + 1)
|
...updatingOwners.slice(ownerPosition + 1)
|
||||||
];
|
];
|
||||||
|
// The list of ldap userNames currently in the list
|
||||||
|
const userNames = updatedOwners.mapBy('userName');
|
||||||
|
// Checks that the userNames are not already in the list of current owners
|
||||||
|
const hasDuplicates = new Set(userNames).size !== userNames.length;
|
||||||
|
|
||||||
|
if (hasDuplicates) {
|
||||||
|
set(
|
||||||
|
this,
|
||||||
|
'errorMessage',
|
||||||
|
'Uh oh! Looks like there are duplicates in the list of owners, please remove them first.'
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Full reset of the `owners` list with the new list
|
// Full reset of the `owners` list with the new list
|
||||||
sourceOwners.setObjects(updatedOwners);
|
sourceOwners.setObjects(updatedOwners);
|
||||||
@ -213,6 +227,7 @@ export default Component.extend({
|
|||||||
* @return {Promise.<void>}
|
* @return {Promise.<void>}
|
||||||
*/
|
*/
|
||||||
async editUserName(currentOwner, userName) {
|
async editUserName(currentOwner, userName) {
|
||||||
|
set(this, 'errorMessage', '');
|
||||||
if (userName) {
|
if (userName) {
|
||||||
// getUser returns a promise, treat as such
|
// getUser returns a promise, treat as such
|
||||||
const getUser = get(this, 'ldapUsers.getPartyEntityWithUserName');
|
const getUser = get(this, 'ldapUsers.getPartyEntityWithUserName');
|
||||||
|
@ -3,9 +3,8 @@ import { makeUrnBreadcrumbs } from 'wherehows-web/utils/entities';
|
|||||||
import { datasetComplianceFor, datasetComplianceSuggestionsFor } from 'wherehows-web/utils/api/datasets/compliance';
|
import { datasetComplianceFor, datasetComplianceSuggestionsFor } from 'wherehows-web/utils/api/datasets/compliance';
|
||||||
import {
|
import {
|
||||||
getDatasetOwners,
|
getDatasetOwners,
|
||||||
getPartyEntities,
|
getUserEntities,
|
||||||
isRequiredMinOwnersNotConfirmed,
|
isRequiredMinOwnersNotConfirmed
|
||||||
getPartyEntitiesMap
|
|
||||||
} from 'wherehows-web/utils/api/datasets/owners';
|
} from 'wherehows-web/utils/api/datasets/owners';
|
||||||
|
|
||||||
const { Route, get, set, setProperties, isPresent, inject: { service }, $: { getJSON } } = Ember;
|
const { Route, get, set, setProperties, isPresent, inject: { service }, $: { getJSON } } = Ember;
|
||||||
@ -395,15 +394,15 @@ export default Route.extend({
|
|||||||
|
|
||||||
// Retrieve the current owners of the dataset and store on the controller
|
// Retrieve the current owners of the dataset and store on the controller
|
||||||
(async id => {
|
(async id => {
|
||||||
const [owners, userEntities] = await Promise.all([getDatasetOwners(id), getPartyEntities()]);
|
const [owners, { userEntitiesSource, userEntitiesMaps }] = await Promise.all([
|
||||||
|
getDatasetOwners(id),
|
||||||
|
getUserEntities()
|
||||||
|
]);
|
||||||
setProperties(controller, {
|
setProperties(controller, {
|
||||||
requiredMinNotConfirmed: isRequiredMinOwnersNotConfirmed(owners),
|
requiredMinNotConfirmed: isRequiredMinOwnersNotConfirmed(owners),
|
||||||
owners,
|
owners,
|
||||||
userEntitiesMaps: getPartyEntitiesMap(userEntities),
|
userEntitiesMaps,
|
||||||
get userEntitiesSource() {
|
userEntitiesSource
|
||||||
// TODO: memoize
|
|
||||||
return Object.keys(this.userEntitiesMaps);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
})(id);
|
})(id);
|
||||||
},
|
},
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
const { isEmpty, Service, $: { getJSON } } = Ember;
|
|
||||||
const partyEntitiesUrl = '/api/v1/party/entities';
|
|
||||||
|
|
||||||
const cache = {
|
|
||||||
// Cache containing results from the last request to partyEntities api
|
|
||||||
partyEntities: null
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Async request for partyEntities. Caches the value from the initial request
|
|
||||||
* @return {Promise.<Object|void>}
|
|
||||||
*/
|
|
||||||
const getLDAPUsers = () =>
|
|
||||||
new Promise(resolve => {
|
|
||||||
const cachedResults = cache.partyEntities;
|
|
||||||
|
|
||||||
// Resolve with cachedResults if this has been previously requested
|
|
||||||
if (!isEmpty(cachedResults)) {
|
|
||||||
return resolve(cachedResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cast $.getJSON to native Promise
|
|
||||||
Promise.resolve(getJSON(partyEntitiesUrl))
|
|
||||||
.then(({ status, userEntities = [] }) => {
|
|
||||||
if (status === 'ok' && userEntities.length) {
|
|
||||||
/**
|
|
||||||
* @type {Object} userEntitiesMaps hash of userEntities: label -> displayName
|
|
||||||
*/
|
|
||||||
const userEntitiesMaps = userEntities.reduce(
|
|
||||||
(map, { label, displayName }) => ((map[label] = displayName), map),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
userEntities,
|
|
||||||
userEntitiesMaps,
|
|
||||||
userEntitiesSource: Object.keys(userEntitiesMaps)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(results => (cache.partyEntities = results))
|
|
||||||
.then(resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes a userNameQuery query and find userNames that match by starting with
|
|
||||||
* the pattern
|
|
||||||
* @param {String} userNameQuery pattern to search for
|
|
||||||
* @param {Function} syncResults callback
|
|
||||||
* @param {Function} asyncResults callback
|
|
||||||
*/
|
|
||||||
const ldapResolver = (userNameQuery, syncResults, asyncResults) => {
|
|
||||||
const regex = new RegExp(`^${userNameQuery}.*`, 'i');
|
|
||||||
|
|
||||||
getLDAPUsers()
|
|
||||||
.then(({ userEntitiesSource = {} }) => userEntitiesSource.filter(entity => regex.test(entity)))
|
|
||||||
.then(asyncResults);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For a given userName, find the userEntity object that contains the userName
|
|
||||||
* @param {String} userName the unique userName
|
|
||||||
* @return {Promise.<TResult|null>} resolves with the userEntity or null otherwise
|
|
||||||
*/
|
|
||||||
const getPartyEntityWithUserName = userName =>
|
|
||||||
getLDAPUsers().then(({ userEntities }) => userEntities.find(({ label }) => label === userName) || null);
|
|
||||||
|
|
||||||
export default Service.extend({
|
|
||||||
getPartyEntityWithUserName,
|
|
||||||
userNamesResolver: ldapResolver,
|
|
||||||
fetchUserNames: getLDAPUsers
|
|
||||||
});
|
|
35
wherehows-web/app/services/user-lookup.ts
Normal file
35
wherehows-web/app/services/user-lookup.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import Ember from 'ember';
|
||||||
|
import { getUserEntities } from 'wherehows-web/utils/api/datasets/owners';
|
||||||
|
import { IPartyEntity, IPartyProps } from 'wherehows-web/typings/api/datasets/party-entities';
|
||||||
|
|
||||||
|
const { Service } = Ember;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a userNameQuery query and find userNames that match by starting with
|
||||||
|
* the pattern
|
||||||
|
* @param {string} userNameQuery pattern to search for
|
||||||
|
* @param {Function} _syncResults callback
|
||||||
|
* @param {Function} asyncResults callback
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
const ldapResolver = async (userNameQuery: string, _syncResults: Function, asyncResults: Function): Promise<void> => {
|
||||||
|
const ldapRegex = new RegExp(`^${userNameQuery}.*`, 'i');
|
||||||
|
const { userEntitiesSource = [] }: IPartyProps = await getUserEntities();
|
||||||
|
asyncResults(userEntitiesSource.filter((entity: string) => ldapRegex.test(entity)));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For a given userName, find the userEntity object that contains the userName
|
||||||
|
* @param {string} userName the unique userName
|
||||||
|
* @return {Promise<IPartyEntity>} resolves with the userEntity or null otherwise
|
||||||
|
*/
|
||||||
|
const getPartyEntityWithUserName = (userName: string): Promise<IPartyEntity | null> =>
|
||||||
|
getUserEntities().then(
|
||||||
|
({ userEntities }: IPartyProps) => userEntities.find(({ label }: { label: string }) => label === userName) || null
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Service.extend({
|
||||||
|
getPartyEntityWithUserName,
|
||||||
|
userNamesResolver: ldapResolver,
|
||||||
|
fetchUserNames: getUserEntities
|
||||||
|
});
|
@ -16,3 +16,19 @@ export interface IPartyEntityResponse {
|
|||||||
status: ApiStatus;
|
status: ApiStatus;
|
||||||
userEntities?: Array<IPartyEntity>;
|
userEntities?: Array<IPartyEntity>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a userEntityMap interface
|
||||||
|
*/
|
||||||
|
export interface userEntityMap {
|
||||||
|
[label: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the props resolved by the getUserEntities function
|
||||||
|
*/
|
||||||
|
export interface IPartyProps {
|
||||||
|
userEntities: Array<IPartyEntity>;
|
||||||
|
userEntitiesMaps: userEntityMap;
|
||||||
|
userEntitiesSource: Array<keyof userEntityMap>;
|
||||||
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
import { ApiRoot, ApiStatus } from 'wherehows-web/utils/api/shared';
|
import { ApiRoot, ApiStatus } from 'wherehows-web/utils/api/shared';
|
||||||
import { datasetUrlById } from 'wherehows-web/utils/api/datasets/shared';
|
import { datasetUrlById } from 'wherehows-web/utils/api/datasets/shared';
|
||||||
import { IPartyEntity, IPartyEntityResponse } from 'wherehows-web/typings/api/datasets/party-entities';
|
import {
|
||||||
|
IPartyEntity,
|
||||||
|
IPartyEntityResponse,
|
||||||
|
IPartyProps,
|
||||||
|
userEntityMap
|
||||||
|
} from 'wherehows-web/typings/api/datasets/party-entities';
|
||||||
import { IOwner, IOwnerResponse } from 'wherehows-web/typings/api/datasets/owners';
|
import { IOwner, IOwnerResponse } from 'wherehows-web/typings/api/datasets/owners';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,12 +53,64 @@ export const getPartyEntities = async (): Promise<Array<IPartyEntity>> => {
|
|||||||
return status === ApiStatus.OK ? userEntities : Promise.reject(status);
|
return status === ApiStatus.OK ? userEntities : Promise.reject(status);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IIFE prepares the environment scope and returns a closure function that ensures that
|
||||||
|
* there is ever only one inflight request for userEntities.
|
||||||
|
* Resolves all subsequent calls with the result for the initial invocation.
|
||||||
|
* userEntitiesSource property is also lazy evaluated and cached for app lifetime.
|
||||||
|
* @type {() => Promise<IPartyProps>}
|
||||||
|
*/
|
||||||
|
export const getUserEntities: () => Promise<IPartyProps> = (() => {
|
||||||
|
/**
|
||||||
|
* Memoized reference to the resolved value of a previous invocation to curried function in getUserEntities
|
||||||
|
* @type {{result: IPartyProps | null}}
|
||||||
|
*/
|
||||||
|
const cache: { result: IPartyProps | null; userEntitiesSource: Array<keyof userEntityMap> } = {
|
||||||
|
result: null,
|
||||||
|
userEntitiesSource: []
|
||||||
|
};
|
||||||
|
let inflightRequest: Promise<Array<IPartyEntity>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes the requestor for party entities, and adds perf optimizations listed above
|
||||||
|
* @return {Promise<IPartyProps>}
|
||||||
|
*/
|
||||||
|
return async (): Promise<IPartyProps> => {
|
||||||
|
// If a previous request has already resolved, return the cached value
|
||||||
|
if (cache.result) {
|
||||||
|
return cache.result;
|
||||||
|
}
|
||||||
|
// If we don't already have a previous api request for party entities,
|
||||||
|
// assign a new one to free variable
|
||||||
|
if (!inflightRequest) {
|
||||||
|
inflightRequest = getPartyEntities();
|
||||||
|
}
|
||||||
|
|
||||||
|
const userEntities: Array<IPartyEntity> = await inflightRequest;
|
||||||
|
|
||||||
|
return (cache.result = {
|
||||||
|
userEntities,
|
||||||
|
userEntitiesMaps: getPartyEntitiesMap(userEntities),
|
||||||
|
// userEntitiesSource is not usually needed immediately
|
||||||
|
// hence using a getter for lazy evaluation
|
||||||
|
get userEntitiesSource() {
|
||||||
|
const userEntitiesSource = cache.userEntitiesSource;
|
||||||
|
if (userEntitiesSource.length) {
|
||||||
|
return userEntitiesSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (cache.userEntitiesSource = Object.keys(this.userEntitiesMaps));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a list of party entities into a map of entity label to displayName value
|
* Transforms a list of party entities into a map of entity label to displayName value
|
||||||
* @param {Array<IPartyEntity>} partyEntities
|
* @param {Array<IPartyEntity>} partyEntities
|
||||||
* @return {Object<string>}
|
* @return {Object<string>}
|
||||||
*/
|
*/
|
||||||
export const getPartyEntitiesMap = (partyEntities: Array<IPartyEntity>): { [label: string]: string } =>
|
export const getPartyEntitiesMap = (partyEntities: Array<IPartyEntity>): userEntityMap =>
|
||||||
partyEntities.reduce(
|
partyEntities.reduce(
|
||||||
(map: { [label: string]: string }, { label, displayName }: IPartyEntity) => ((map[label] = displayName), map),
|
(map: { [label: string]: string }, { label, displayName }: IPartyEntity) => ((map[label] = displayName), map),
|
||||||
{}
|
{}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user