web/satellite: usage api refactored (#2864)

This commit is contained in:
Nikolay Yurchenko 2019-08-28 12:53:53 +03:00 committed by GitHub
parent 8fbb25f3b5
commit 8c24399438
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 366 additions and 167 deletions

View File

@ -3,15 +3,21 @@
import apollo from '@/utils/apollo';
import gql from 'graphql-tag';
import { RequestResponse } from '@/types/response';
import { ProjectUsage, UsageApi } from '@/types/usage';
import { BaseGql } from '@/api/baseGql';
// fetchProjectUsage retrieves total project usage for a given period
export async function fetchProjectUsage(projectId: string, since: Date, before: Date): Promise<RequestResponse<ProjectUsage>> {
let result: RequestResponse<ProjectUsage> = new RequestResponse<ProjectUsage>();
let response: any = await apollo.query(
{
query: gql(`
/**
* Exposes all project-usage-related functionality
*/
export class ProjectUsageApiGql extends BaseGql implements UsageApi {
/**
* Fetch usage
*
* @returns ProjectUsage
* @throws Error
*/
public async get(projectId: string, since: Date, before: Date): Promise<ProjectUsage> {
const query = `
query($projectId: String!, $since: DateTime!, $before: DateTime!) {
project(id: $projectId) {
usage(since: $since, before: $before) {
@ -22,24 +28,16 @@ export async function fetchProjectUsage(projectId: string, since: Date, before:
before
}
}
}`
),
variables: {
projectId: projectId,
since: since.toISOString(),
before: before.toISOString()
},
fetchPolicy: 'no-cache',
errorPolicy: 'all'
}
);
}`;
if (response.errors) {
result.errorMessage = response.errors[0].message;
} else {
result.isSuccess = true;
result.data = response.data.project.usage;
}
const variables = {
projectId,
since,
before
};
return result;
const response = await this.query(query, variables);
return response.data.project.usage;
}
}

View File

@ -26,12 +26,12 @@
NOTIFICATION_ACTIONS,
PM_ACTIONS,
API_KEYS_ACTIONS,
PROJECT_USAGE_ACTIONS,
PROJECT_PAYMENT_METHODS_ACTIONS
} from '@/utils/constants/actionNames';
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { Project } from '@/types/projects';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { PROJECT_USAGE_ACTIONS } from '@/store/modules/usage';
@Component
export default class ProjectSelectionDropdown extends Vue {
@ -42,8 +42,12 @@
this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_PROJECTS);
this.$store.dispatch(PM_ACTIONS.SET_SEARCH_QUERY, '');
// TODO: add types
const usageResponse = await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP);
try {
await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP);
} catch (err) {
await this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, `Unable to fetch project usage. ${err.message}`);
}
const paymentMethodsResponse = await this.$store.dispatch(PROJECT_PAYMENT_METHODS_ACTIONS.FETCH);
try {
@ -58,10 +62,6 @@
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch api keys');
}
if (!usageResponse.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch project usage');
}
try {
await this.$store.dispatch(BUCKET_ACTIONS.FETCH, 1);
} catch (error) {

View File

@ -24,7 +24,7 @@
</div>
<router-link class="navigation-area__item-container" v-bind:class="{disabled: isProjectNotSelected && navItem.path !== '/project-details'}" v-for="navItem in navigation" v-bind:key="navItem.name" :to="navItem.path">
<div class="navigation-area__item-container__link-container" >
<div v-html="navItem.svg"></div>
<div v-html="navItem.icon"></div>
<h1>{{navItem.name}}</h1>
<div class="navigation-area__item-container__link-container__add-button" id="addTeamMemberPopupButtonSVG" v-if="navItem.name === 'Team'">
<div :click="togglePopup">

View File

@ -49,7 +49,6 @@
API_KEYS_ACTIONS,
APP_STATE_ACTIONS,
NOTIFICATION_ACTIONS,
PROJECT_USAGE_ACTIONS,
PM_ACTIONS,
} from '@/utils/constants/actionNames';
import Button from '@/components/common/Button.vue';
@ -58,6 +57,7 @@
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { CreateProjectModel, Project } from '@/types/projects';
import { PROJECT_USAGE_ACTIONS } from '@/store/modules/usage';
@Component({
components: {

View File

@ -60,8 +60,10 @@
import { Component, Vue } from 'vue-property-decorator';
import { RouteConfig } from '@/router';
import Datepicker from '@/components/project/DatePicker.vue';
import { NOTIFICATION_ACTIONS, PROJECT_USAGE_ACTIONS } from '@/utils/constants/actionNames';
import { NOTIFICATION_ACTIONS } from '@/utils/constants/actionNames';
import { toUnixTimestamp } from '@/utils/time';
import { PROJECT_USAGE_ACTIONS } from '@/store/modules/usage';
import { DateRange } from '@/types/usage';
@Component({
components: {
@ -107,12 +109,20 @@
return this.$store.state.usageModule.projectUsage.objectCount.toPrecision(5);
}
public mounted(): void {
this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP);
public async mounted(): Promise<void> {
try {
await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP);
} catch (e) {
await this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, `Unable to fetch project usage. ${e.message}`);
}
}
public beforeRouteLeave(to, from, next): void {
this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP, this.dateRange);
public async beforeRouteLeave(to, from, next): Promise<void> {
try {
await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP, this.dateRange);
} catch (e) {
await this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, e.message);
}
const buttons = [...(document as any).querySelectorAll('.usage-report-container__options-area__option')];
buttons.forEach(option => {
@ -130,18 +140,20 @@
public async onCurrentRollupClick(event: any): Promise<void> {
this.onButtonClickAction(event);
const response = await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP);
if (!response.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch project usage');
try {
await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP);
} catch (e) {
await this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, `Unable to fetch project usage. ${e.message}`);
}
}
public async onPreviousRollupClick(event: any): Promise<void> {
this.onButtonClickAction(event);
const response = await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_PREVIOUS_ROLLUP);
if (!response.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch project usage');
try {
await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_PREVIOUS_ROLLUP);
} catch (e) {
await this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, `Unable to fetch project usage. ${e.message}`);
}
}
@ -175,15 +187,19 @@
let endDate = isInverted ? firstDate : secondDate;
endDate = new Date(Date.UTC(endDate.getUTCFullYear(), endDate.getUTCMonth(), endDate.getUTCDate(), 23, 59, 59));
if (now.getUTCFullYear() === endDate.getUTCFullYear() &&
now.getUTCMonth() === endDate.getUTCMonth() &&
now.getUTCDate() === endDate.getUTCDate()) {
endDate = now;
}
const response = await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH, {startDate, endDate});
if (!response.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch project usage');
const dateRange: DateRange = new DateRange(startDate, endDate);
try {
await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH, dateRange);
} catch (e) {
await this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, `Unable to fetch project usage. ${e.message}`);
}
}

View File

@ -17,9 +17,10 @@ import { makeBucketsModule } from '@/store/modules/buckets';
import { projectPaymentsMethodsModule } from '@/store/modules/paymentMethods';
import { makeProjectMembersModule } from '@/store/modules/projectMembers';
import { makeProjectsModule } from '@/store/modules/projects';
import { usageModule } from '@/store/modules/usage';
import { makeUsageModule } from '@/store/modules/usage';
import { makeUsersModule } from '@/store/modules/users';
import { ProjectsApiGql } from '@/api/projects';
import { ProjectUsageApiGql } from '@/api/usage';
Vue.use(Vuex);
@ -37,6 +38,7 @@ const creditsApi = new CreditsApiGql();
const bucketsApi = new BucketsApiGql();
const projectMembersApi = new ProjectMembersApiGql();
const projectsApi = new ProjectsApiGql();
const projectUsageApi = new ProjectUsageApiGql();
// Satellite store (vuex)
const store = new Vuex.Store({
@ -49,7 +51,7 @@ const store = new Vuex.Store({
projectPaymentsMethodsModule,
usersModule: makeUsersModule(usersApi),
projectsModule: makeProjectsModule(projectsApi),
usageModule,
usageModule: makeUsageModule(projectUsageApi),
bucketUsageModule: makeBucketsModule(bucketsApi),
}
});

View File

@ -1,78 +1,91 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
import { PROJECT_USAGE_MUTATIONS } from '@/store/mutationConstants';
import { PROJECT_USAGE_ACTIONS } from '@/utils/constants/actionNames';
import { fetchProjectUsage } from '@/api/usage';
import { RequestResponse } from '@/types/response';
import { ProjectUsageApiGql } from '@/api/usage';
import { StoreModule } from '@/store';
import { ProjectUsage, DateRange } from '@/types/usage';
export const usageModule = {
state: {
projectUsage: {storage: 0, egress: 0, objectCount: 0} as ProjectUsage,
startDate: new Date(),
endDate: new Date()
},
export const PROJECT_USAGE_ACTIONS = {
FETCH: 'fetchProjectUsage',
FETCH_CURRENT_ROLLUP: 'fetchCurrentProjectUsage',
FETCH_PREVIOUS_ROLLUP: 'fetchPreviousProjectUsage',
CLEAR: 'clearProjectUsage',
};
export const PROJECT_USAGE_MUTATIONS = {
SET_PROJECT_USAGE: 'SET_PROJECT_USAGE',
SET_DATE: 'SET_DATE_PROJECT_USAGE',
CLEAR: 'CLEAR_PROJECT_USAGE'
};
const defaultState = new ProjectUsage(0, 0, 0, new Date(), new Date());
class UsageState {
public projectUsage: ProjectUsage = defaultState;
public startDate: Date = new Date();
public endDate: Date = new Date();
}
export function makeUsageModule(api: ProjectUsageApiGql): StoreModule<UsageState> {
return {
state: new UsageState(),
mutations: {
[PROJECT_USAGE_MUTATIONS.FETCH](state: any, projectUsage: ProjectUsage) {
[PROJECT_USAGE_MUTATIONS.SET_PROJECT_USAGE](state: UsageState, projectUsage: ProjectUsage) {
state.projectUsage = projectUsage;
},
// TODO: create type here instead of {startDate, endDate}
[PROJECT_USAGE_MUTATIONS.SET_DATE](state: any, {startDate, endDate}: any) {
state.startDate = startDate as Date;
state.endDate = endDate as Date;
[PROJECT_USAGE_MUTATIONS.SET_DATE](state: UsageState, dateRange: DateRange) {
state.startDate = dateRange.startDate;
state.endDate = dateRange.endDate;
},
[PROJECT_USAGE_MUTATIONS.CLEAR](state:any) {
state.projectUsage = {storage: 0, egress: 0, objectCount: 0} as ProjectUsage;
[PROJECT_USAGE_MUTATIONS.CLEAR](state: UsageState) {
state.projectUsage = defaultState;
state.startDate = new Date();
state.endData = new Date();
state.endDate = new Date();
}
},
actions: {
[PROJECT_USAGE_ACTIONS.FETCH]: async function({commit, rootGetters}: any, {startDate, endDate}: any): Promise<RequestResponse<ProjectUsage>> {
[PROJECT_USAGE_ACTIONS.FETCH]: async function({commit, rootGetters}: any, dateRange: DateRange): Promise<ProjectUsage> {
const projectID = rootGetters.selectedProject.id;
let result: RequestResponse<ProjectUsage> = await fetchProjectUsage(projectID, startDate, endDate);
let usage: ProjectUsage = await api.get(projectID, dateRange.startDate, dateRange.endDate);
if (result.isSuccess) {
commit(PROJECT_USAGE_MUTATIONS.SET_DATE, {startDate, endDate});
commit(PROJECT_USAGE_MUTATIONS.FETCH, result.data);
}
commit(PROJECT_USAGE_MUTATIONS.SET_DATE, dateRange);
commit(PROJECT_USAGE_MUTATIONS.SET_PROJECT_USAGE, usage);
return result;
return usage;
},
[PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP]: async function({commit, rootGetters}: any): Promise<RequestResponse<ProjectUsage>> {
[PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP]: async function({commit, rootGetters}: any): Promise<ProjectUsage> {
const projectID: string = rootGetters.selectedProject.id;
const endDate = new Date();
const startDate = new Date(Date.UTC(endDate.getUTCFullYear(), endDate.getUTCMonth(), 1));
const dateRange = new DateRange(startDate, endDate);
let result: RequestResponse<ProjectUsage> = await fetchProjectUsage(projectID, startDate, endDate);
let usage: ProjectUsage = await api.get(projectID, dateRange.startDate, dateRange.endDate);
if (result.isSuccess) {
commit(PROJECT_USAGE_MUTATIONS.SET_DATE, {startDate, endDate});
commit(PROJECT_USAGE_MUTATIONS.FETCH, result.data);
}
commit(PROJECT_USAGE_MUTATIONS.SET_DATE, dateRange);
commit(PROJECT_USAGE_MUTATIONS.SET_PROJECT_USAGE, usage);
return result;
return usage;
},
[PROJECT_USAGE_ACTIONS.FETCH_PREVIOUS_ROLLUP]: async function({commit, rootGetters}: any): Promise<RequestResponse<ProjectUsage>> {
[PROJECT_USAGE_ACTIONS.FETCH_PREVIOUS_ROLLUP]: async function({commit, rootGetters}: any): Promise<ProjectUsage> {
const projectID = rootGetters.selectedProject.id;
const date = new Date();
const startDate = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() - 1, 1));
const endDate = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 0, 23, 59, 59));
const dateRange = new DateRange(startDate, endDate);
let result = await fetchProjectUsage(projectID, startDate, endDate);
let usage: ProjectUsage = await api.get(projectID, dateRange.startDate, dateRange.endDate);
if (result.isSuccess) {
commit(PROJECT_USAGE_MUTATIONS.SET_DATE, {startDate, endDate});
commit(PROJECT_USAGE_MUTATIONS.FETCH, result.data);
}
commit(PROJECT_USAGE_MUTATIONS.SET_DATE, dateRange);
commit(PROJECT_USAGE_MUTATIONS.SET_PROJECT_USAGE, usage);
return result;
return usage;
},
[PROJECT_USAGE_ACTIONS.CLEAR]: function({commit}): void {
commit(PROJECT_USAGE_MUTATIONS.CLEAR);
}
}
};
},
};
}

View File

@ -10,12 +10,6 @@ export const API_KEYS_MUTATIONS = {
CLEAR: 'clear',
};
export const PROJECT_USAGE_MUTATIONS = {
FETCH: 'FETCH_PROJECT_USAGE',
SET_DATE: 'SET_DATE_PROJECT_USAGE',
CLEAR: 'CLEAR_PROJECT_USAGE'
};
export const NOTIFICATION_MUTATIONS = {
ADD: 'ADD_NOTIFICATION',
DELETE: 'DELETE_NOTIFICATION',

View File

@ -2,7 +2,7 @@
// See LICENSE for copying information.
/**
* Exposes all user-related functionality
* Exposes all project-related functionality
*/
export interface ProjectsApi {
/**

View File

@ -1,11 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
// ProjectUsage sums usage for given period
declare type ProjectUsage = {
storage: number,
egress: number,
objectCount: number,
since: Date,
before: Date,
};

View File

@ -0,0 +1,42 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
// ProjectUsage sums usage for given period
export class ProjectUsage {
public storage: number;
public egress: number;
public objectCount: number;
public since: Date;
public before: Date;
public constructor(storage: number, egress: number, objectCount: number, since: Date, before: Date) {
this.storage = storage;
this.egress = egress;
this.objectCount = objectCount;
this.since = since;
this.before = before;
}
}
export class DateRange {
public startDate: Date = new Date();
public endDate: Date = new Date();
public constructor(startDate: Date, endDate: Date) {
this.startDate = startDate;
this.endDate = endDate;
}
}
/**
* Exposes all project-usage-related functionality
*/
export interface UsageApi {
/**
* Fetch usage
*
* @returns ProjectUsage
* @throws Error
*/
get(projectId: string, since: Date, before: Date): Promise<ProjectUsage>;
}

View File

@ -54,13 +54,6 @@ export const API_KEYS_ACTIONS = {
CLEAR_SELECTION: 'clearAPIKeySelection'
};
export const PROJECT_USAGE_ACTIONS = {
FETCH: 'fetchProjectUsage',
FETCH_CURRENT_ROLLUP: 'fetchCurrentProjectUsage',
FETCH_PREVIOUS_ROLLUP: 'fetchPreviousProjectUsage',
CLEAR: 'clearProjectUsage',
};
export const PROJECT_PAYMENT_METHODS_ACTIONS = {
ADD: 'addProjectPaymentMethod',
FETCH: 'fetchProjectPaymentMethods',

View File

@ -29,7 +29,6 @@
APP_STATE_ACTIONS,
NOTIFICATION_ACTIONS,
PM_ACTIONS,
PROJECT_USAGE_ACTIONS,
PROJECT_PAYMENT_METHODS_ACTIONS,
} from '@/utils/constants/actionNames';
import { USER_ACTIONS } from '@/store/modules/users';
@ -37,18 +36,16 @@
import { AppState } from '@/utils/constants/appStateEnum';
import { AuthToken } from '@/utils/authToken';
import { Project } from '@/types/projects';
import { RequestResponse } from '@/types/response';
import router, { RouteConfig } from '@/router';
import { RouteConfig } from '@/router';
import { User } from '@/types/users';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { PROJECT_USAGE_ACTIONS } from '@/store/modules/usage';
@Component({
mounted: async function() {
setTimeout(async () => {
let user: User;
try {
user = await this.$store.dispatch(USER_ACTIONS.GET);
await this.$store.dispatch(USER_ACTIONS.GET);
} catch (error) {
this.$store.dispatch(APP_STATE_ACTIONS.CHANGE_STATE, AppState.ERROR);
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, error.message);
@ -89,9 +86,10 @@
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch api keys');
}
const usageResponse = await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP);
if (!usageResponse.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch project usage');
try {
await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP);
} catch (e) {
await this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, `Unable to fetch project usage. ${e.message}`);
}
try {

View File

@ -0,0 +1,154 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import { createLocalVue } from '@vue/test-utils';
import { ProjectUsageApiGql } from '@/api/usage';
import { makeUsageModule, PROJECT_USAGE_ACTIONS, PROJECT_USAGE_MUTATIONS } from '@/store/modules/usage';
import { DateRange, ProjectUsage } from '@/types/usage';
import { ProjectsApiGql } from '@/api/projects';
import { makeProjectsModule } from '@/store/modules/projects';
import { Project } from '@/types/projects';
const Vue = createLocalVue();
const projectUsageApi = new ProjectUsageApiGql();
const usageModule = makeUsageModule(projectUsageApi);
const projectsApi = new ProjectsApiGql();
const projectsModule = makeProjectsModule(projectsApi);
const selectedProject = new Project('', '', '', '');
selectedProject.id = '1';
projectsModule.state.selectedProject = selectedProject;
let testDate1 = new Date();
testDate1.setDate(1);
let testDate2 = new Date();
testDate2.setDate(2);
const testUsage = new ProjectUsage(2, 3, 4, testDate1, testDate2);
const now = new Date();
Vue.use(Vuex);
const store = new Vuex.Store({ modules: { usageModule, projectsModule } });
const state = (store.state as any).usageModule;
describe('mutations', () => {
beforeEach(() => {
createLocalVue().use(Vuex);
});
it('fetch project usage', () => {
store.commit(PROJECT_USAGE_MUTATIONS.SET_PROJECT_USAGE, testUsage);
expect(state.projectUsage.storage).toBe(2);
expect(state.projectUsage.egress).toBe(3);
expect(state.projectUsage.objectCount).toBe(4);
expect(state.startDate.toDateString()).toBe(now.toDateString());
expect(state.endDate.toDateString()).toBe(now.toDateString());
});
it('set dates', () => {
const dateRange: DateRange = new DateRange(testDate1, testDate2);
store.commit(PROJECT_USAGE_MUTATIONS.SET_DATE, dateRange);
expect(state.startDate.toDateString()).toBe(testDate1.toDateString());
expect(state.endDate.toDateString()).toBe(testDate2.toDateString());
});
it('clear usage', () => {
store.commit(PROJECT_USAGE_MUTATIONS.CLEAR);
expect(state.projectUsage.storage).toBe(0);
expect(state.projectUsage.egress).toBe(0);
expect(state.projectUsage.objectCount).toBe(0);
expect(state.startDate.toDateString()).toBe(now.toDateString());
expect(state.endDate.toDateString()).toBe(now.toDateString());
});
});
describe('actions', () => {
beforeEach(() => {
jest.resetAllMocks();
createLocalVue().use(Vuex);
});
it('success fetch project usage', async () => {
jest.spyOn(projectUsageApi, 'get').mockReturnValue(
Promise.resolve(testUsage)
);
const dateRange: DateRange = new DateRange(testDate1, testDate2);
await store.dispatch(PROJECT_USAGE_ACTIONS.FETCH, dateRange);
expect(state.projectUsage.storage).toBe(2);
expect(state.projectUsage.egress).toBe(3);
expect(state.projectUsage.objectCount).toBe(4);
expect(state.startDate.toDateString()).toBe(testDate1.toDateString());
expect(state.endDate.toDateString()).toBe(testDate2.toDateString());
});
it('success fetch current project usage', async () => {
jest.spyOn(projectUsageApi, 'get').mockReturnValue(
Promise.resolve(testUsage)
);
const firstDate = new Date();
firstDate.setDate(1);
await store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_CURRENT_ROLLUP);
expect(state.projectUsage.storage).toBe(2);
expect(state.projectUsage.egress).toBe(3);
expect(state.projectUsage.objectCount).toBe(4);
expect(state.startDate.toDateString()).toBe(firstDate.toDateString());
expect(state.endDate.toDateString()).toBe(now.toDateString());
});
it('success fetch previous project usage', async () => {
jest.spyOn(projectUsageApi, 'get').mockReturnValue(
Promise.resolve(testUsage)
);
const firstDate = new Date();
firstDate.setMonth(firstDate.getMonth() - 1);
firstDate.setDate(1);
const secondDate = new Date();
secondDate.setDate(1);
await store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_PREVIOUS_ROLLUP);
expect(state.projectUsage.storage).toBe(2);
expect(state.projectUsage.egress).toBe(3);
expect(state.projectUsage.objectCount).toBe(4);
expect(state.startDate.toDateString()).toBe(firstDate.toDateString());
expect(state.endDate.toDateString()).toBe(secondDate.toDateString());
});
it('success clear usage', async () => {
await store.dispatch(PROJECT_USAGE_ACTIONS.CLEAR);
expect(state.projectUsage.storage).toBe(0);
expect(state.projectUsage.egress).toBe(0);
expect(state.projectUsage.objectCount).toBe(0);
expect(state.startDate.toDateString()).toBe(now.toDateString());
expect(state.endDate.toDateString()).toBe(now.toDateString());
});
it('create throws an error when create api call fails', async () => {
state.projects = [];
jest.spyOn(projectUsageApi, 'get').mockImplementation(() => { throw new Error(); });
try {
await store.dispatch(PROJECT_USAGE_ACTIONS.FETCH_PREVIOUS_ROLLUP);
expect(true).toBe(false);
} catch (error) {
expect(state.projectUsage.storage).toBe(0);
expect(state.projectUsage.egress).toBe(0);
expect(state.projectUsage.objectCount).toBe(0);
expect(state.startDate.toDateString()).toBe(now.toDateString());
expect(state.endDate.toDateString()).toBe(now.toDateString());
}
});
});