Commit 43a28cfe authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'winh-frontend-user-cache' into 'master'

Introduce UsersCache for storing user entities in frontend

See merge request !11404
parents c8666741 d7f9b408
class AjaxCache { import Cache from './cache';
class AjaxCache extends Cache {
constructor() { constructor() {
this.internalStorage = { }; super();
this.pendingRequests = { }; this.pendingRequests = { };
} }
get(endpoint) {
return this.internalStorage[endpoint];
}
hasData(endpoint) {
return Object.prototype.hasOwnProperty.call(this.internalStorage, endpoint);
}
remove(endpoint) {
delete this.internalStorage[endpoint];
}
retrieve(endpoint) { retrieve(endpoint) {
if (this.hasData(endpoint)) { if (this.hasData(endpoint)) {
return Promise.resolve(this.get(endpoint)); return Promise.resolve(this.get(endpoint));
......
class Cache {
constructor() {
this.internalStorage = { };
}
get(key) {
return this.internalStorage[key];
}
hasData(key) {
return Object.prototype.hasOwnProperty.call(this.internalStorage, key);
}
remove(key) {
delete this.internalStorage[key];
}
}
export default Cache;
import Api from '../../api';
import Cache from './cache';
class UsersCache extends Cache {
retrieve(username) {
if (this.hasData(username)) {
return Promise.resolve(this.get(username));
}
return Api.users('', { username })
.then((users) => {
if (!users.length) {
throw new Error(`User "${username}" could not be found!`);
}
if (users.length > 1) {
throw new Error(`Expected username "${username}" to be unique!`);
}
const user = users[0];
this.internalStorage[username] = user;
return user;
});
// missing catch is intentional, error handling depends on use case
}
}
export default new UsersCache();
import Cache from '~/lib/utils/cache';
describe('Cache', () => {
const dummyKey = 'just some key';
const dummyValue = 'more than a value';
let cache;
beforeEach(() => {
cache = new Cache();
});
describe('get', () => {
it('return cached data', () => {
cache.internalStorage[dummyKey] = dummyValue;
expect(cache.get(dummyKey)).toBe(dummyValue);
});
it('returns undefined for missing data', () => {
expect(cache.internalStorage[dummyKey]).toBe(undefined);
expect(cache.get(dummyKey)).toBe(undefined);
});
});
describe('hasData', () => {
it('return true for cached data', () => {
cache.internalStorage[dummyKey] = dummyValue;
expect(cache.hasData(dummyKey)).toBe(true);
});
it('returns false for missing data', () => {
expect(cache.internalStorage[dummyKey]).toBe(undefined);
expect(cache.hasData(dummyKey)).toBe(false);
});
});
describe('remove', () => {
it('removes data from cache', () => {
cache.internalStorage[dummyKey] = dummyValue;
cache.remove(dummyKey);
expect(cache.internalStorage[dummyKey]).toBe(undefined);
});
it('does nothing for missing data', () => {
expect(cache.internalStorage[dummyKey]).toBe(undefined);
cache.remove(dummyKey);
expect(cache.internalStorage[dummyKey]).toBe(undefined);
});
it('does not remove wrong data', () => {
cache.internalStorage[dummyKey] = dummyValue;
cache.internalStorage[dummyKey + dummyKey] = dummyValue + dummyValue;
cache.remove(dummyKey);
expect(cache.internalStorage[dummyKey]).toBe(undefined);
expect(cache.internalStorage[dummyKey + dummyKey]).toBe(dummyValue + dummyValue);
});
});
});
import Api from '~/api';
import UsersCache from '~/lib/utils/users_cache';
describe('UsersCache', () => {
const dummyUsername = 'win';
const dummyUser = 'has a farm';
beforeEach(() => {
UsersCache.internalStorage = { };
});
describe('get', () => {
it('returns undefined for empty cache', () => {
expect(UsersCache.internalStorage).toEqual({ });
const user = UsersCache.get(dummyUsername);
expect(user).toBe(undefined);
});
it('returns undefined for missing user', () => {
UsersCache.internalStorage['no body'] = 'no data';
const user = UsersCache.get(dummyUsername);
expect(user).toBe(undefined);
});
it('returns matching user', () => {
UsersCache.internalStorage[dummyUsername] = dummyUser;
const user = UsersCache.get(dummyUsername);
expect(user).toBe(dummyUser);
});
});
describe('hasData', () => {
it('returns false for empty cache', () => {
expect(UsersCache.internalStorage).toEqual({ });
expect(UsersCache.hasData(dummyUsername)).toBe(false);
});
it('returns false for missing user', () => {
UsersCache.internalStorage['no body'] = 'no data';
expect(UsersCache.hasData(dummyUsername)).toBe(false);
});
it('returns true for matching user', () => {
UsersCache.internalStorage[dummyUsername] = dummyUser;
expect(UsersCache.hasData(dummyUsername)).toBe(true);
});
});
describe('remove', () => {
it('does nothing if cache is empty', () => {
expect(UsersCache.internalStorage).toEqual({ });
UsersCache.remove(dummyUsername);
expect(UsersCache.internalStorage).toEqual({ });
});
it('does nothing if cache contains no matching data', () => {
UsersCache.internalStorage['no body'] = 'no data';
UsersCache.remove(dummyUsername);
expect(UsersCache.internalStorage['no body']).toBe('no data');
});
it('removes matching data', () => {
UsersCache.internalStorage[dummyUsername] = dummyUser;
UsersCache.remove(dummyUsername);
expect(UsersCache.internalStorage).toEqual({ });
});
});
describe('retrieve', () => {
let apiSpy;
beforeEach(() => {
spyOn(Api, 'users').and.callFake((query, options) => apiSpy(query, options));
});
it('stores and returns data from API call if cache is empty', (done) => {
apiSpy = (query, options) => {
expect(query).toBe('');
expect(options).toEqual({ username: dummyUsername });
return Promise.resolve([dummyUser]);
};
UsersCache.retrieve(dummyUsername)
.then((user) => {
expect(user).toBe(dummyUser);
expect(UsersCache.internalStorage[dummyUsername]).toBe(dummyUser);
})
.then(done)
.catch(done.fail);
});
it('returns undefined if Ajax call fails and cache is empty', (done) => {
const dummyError = new Error('server exploded');
apiSpy = (query, options) => {
expect(query).toBe('');
expect(options).toEqual({ username: dummyUsername });
return Promise.reject(dummyError);
};
UsersCache.retrieve(dummyUsername)
.then(user => fail(`Received unexpected user: ${JSON.stringify(user)}`))
.catch((error) => {
expect(error).toBe(dummyError);
})
.then(done)
.catch(done.fail);
});
it('makes no Ajax call if matching data exists', (done) => {
UsersCache.internalStorage[dummyUsername] = dummyUser;
apiSpy = () => fail(new Error('expected no Ajax call!'));
UsersCache.retrieve(dummyUsername)
.then((user) => {
expect(user).toBe(dummyUser);
})
.then(done)
.catch(done.fail);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment