add project usage to web app (#1669)

This commit is contained in:
Yaroslav Vorobiov 2019-04-05 14:24:34 +03:00 committed by GitHub
parent 92c1121072
commit bc7848fcaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 171 additions and 34 deletions

View File

@ -0,0 +1,43 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
import apollo from '@/utils/apolloManager';
import gql from 'graphql-tag';
// 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> = {
errorMessage: '',
isSuccess: false,
data: {} as ProjectUsage
};
let response: any = await apollo.query(
{
query: gql(`
query {
project(id: "${projectID}") {
usage(since: "${since.toISOString()}", before: "${before.toISOString()}") {
storage,
egress,
objectsCount,
since,
before
}
}
}`
),
fetchPolicy: 'no-cache',
errorPolicy: 'all'
}
);
if (response.errors) {
result.errorMessage = response.errors[0].message;
} else {
result.isSuccess = true;
result.data = response.data.project.usage;
}
return result;
}

View File

@ -32,16 +32,16 @@
<div class="usage-report-container__main-area"> <div class="usage-report-container__main-area">
<div class="usage-report-container__main-area__info-area"> <div class="usage-report-container__main-area__info-area">
<div class="usage-report-container__main-area__info-area__item"> <div class="usage-report-container__main-area__info-area__item">
<h1>Storage GB/H</h1> <h1>Storage GB*H</h1>
<h2>200.23</h2> <h2>{{storage}}</h2>
</div> </div>
<div class="usage-report-container__main-area__info-area__item"> <div class="usage-report-container__main-area__info-area__item">
<h1>Egress GB/H</h1> <h1>Egress GB</h1>
<h2>944.23</h2> <h2>{{egress}}</h2>
</div> </div>
<div class="usage-report-container__main-area__info-area__item"> <div class="usage-report-container__main-area__info-area__item">
<h1>Objects Count/H</h1> <h1>Objects Count*H</h1>
<h2>30 223</h2> <h2>{{objectsCount}}</h2>
</div> </div>
</div> </div>
<div class="usage-report-container__main-area__footer"> <div class="usage-report-container__main-area__footer">
@ -65,51 +65,62 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; import { Component, Vue } from 'vue-property-decorator';
import ROUTES from '@/utils/constants/routerConstants'; import ROUTES from '@/utils/constants/routerConstants';
import Datepicker from '@/components/project/DatePicker.vue'; import Datepicker from '@/components/project/DatePicker.vue';
import { NOTIFICATION_ACTIONS, PROJECT_USAGE_ACTIONS } from '@/utils/constants/actionNames';
@Component( @Component(
{ {
data: function () { data: function () {
const currentDate = new Date(); const currentDate = new Date();
const previousDate = new Date(); const previousDate = new Date();
previousDate.setMonth(currentDate.getMonth() - 1); previousDate.setMonth(currentDate.getMonth() - 1);
return { return {
startTime: { startTime: {
time: '', time: '',
}, },
dateRange: { dateRange: {
startDate: previousDate, startDate: previousDate,
endDate: currentDate, endDate: currentDate,
} },
}; };
}, },
components: { components: {
Datepicker, Datepicker,
}, },
methods: { methods: {
getDates: function(datesArray: string[]): void { getDates: async function(datesArray: string[]) {
const firstDate = new Date(datesArray[0]); const firstDate = new Date(datesArray[0]);
const secondDate = new Date(datesArray[1]); const secondDate = new Date(datesArray[1]);
const isInverted = firstDate > secondDate; const isInverted = firstDate > secondDate;
this.$data.dateRange.startDate = isInverted ? secondDate : firstDate; this.$data.dateRange.startDate = isInverted ? secondDate : firstDate;
this.$data.dateRange.endDate = isInverted ? firstDate : secondDate; this.$data.dateRange.endDate = isInverted ? firstDate : secondDate;
const response = await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH, this.$data.dateRange);
if (!response.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch project usage');
}
}, },
onBackClick: function (): void { onBackClick: function (): void {
this.$router.push(ROUTES.PROJECT_DETAILS); this.$router.push(ROUTES.PROJECT_DETAILS);
}, },
onCurrentRollupClick: function (event: any) { onCurrentRollupClick: async function (event: any) {
const currentDate = new Date(); const currentDate = new Date();
const previousDate = new Date(); const previousDate = new Date();
previousDate.setMonth(currentDate.getMonth() - 1); previousDate.setMonth(currentDate.getMonth() - 1);
this.$data.dateRange.startDate = previousDate; this.$data.dateRange.startDate = previousDate;
this.$data.dateRange.endDate = currentDate; this.$data.dateRange.endDate = currentDate;
(this as any).onButtonClickAction(event); (this as any).onButtonClickAction(event);
},
onPreviousRollupClick: function (event: any) { const response = await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH, this.$data.dateRange);
if (!response.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch project usage');
}
},
onPreviousRollupClick: async function (event: any) {
const currentDate = new Date(); const currentDate = new Date();
const previousDate = new Date(); const previousDate = new Date();
currentDate.setMonth(currentDate.getMonth() - 1); currentDate.setMonth(currentDate.getMonth() - 1);
@ -118,10 +129,15 @@
this.$data.dateRange.startDate = previousDate; this.$data.dateRange.startDate = previousDate;
this.$data.dateRange.endDate = currentDate; this.$data.dateRange.endDate = currentDate;
(this as any).onButtonClickAction(event); (this as any).onButtonClickAction(event);
const response = await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH, this.$data.dateRange);
if (!response.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch project usage');
}
}, },
onCustomDateClick: function (event: any) { onCustomDateClick: function (event: any) {
(this as any).$refs.datePicker.showCheck(); (this as any).$refs.datePicker.showCheck();
(this as any).onButtonClickAction(event); (this as any).onButtonClickAction(event);
}, },
onButtonClickAction: function (event: any) { onButtonClickAction: function (event: any) {
let eventTarget = event.target; let eventTarget = event.target;
@ -148,6 +164,17 @@
window.open(route.href, '_blank'); window.open(route.href, '_blank');
}, },
}, },
computed: {
storage: function () {
return this.$store.state.usageModule.projectUsage.storage.toPrecision(5);
},
egress: function () {
return this.$store.state.usageModule.projectUsage.egress.toPrecision(5);
},
objectsCount: function () {
return this.$store.state.usageModule.projectUsage.objectsCount.toPrecision(5);
}
}
} }
) )

View File

@ -11,6 +11,7 @@ import { projectMembersModule } from '@/store/modules/projectMembers';
import { notificationsModule } from '@/store/modules/notifications'; import { notificationsModule } from '@/store/modules/notifications';
import { appStateModule } from '@/store/modules/appState'; import { appStateModule } from '@/store/modules/appState';
import { apiKeysModule } from '@/store/modules/apiKeys'; import { apiKeysModule } from '@/store/modules/apiKeys';
import { usageModule } from '@/store/modules/usage';
Vue.use(Vuex); Vue.use(Vuex);
@ -22,7 +23,8 @@ const store = new Vuex.Store({
projectMembersModule, projectMembersModule,
notificationsModule, notificationsModule,
appStateModule, appStateModule,
apiKeysModule apiKeysModule,
usageModule
} }
}); });

View File

@ -0,0 +1,36 @@
// 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';
export const usageModule = {
state: {
projectUsage: {storage: 0, egress: 0, objectsCount: 0} as ProjectUsage
},
mutations: {
[PROJECT_USAGE_MUTATIONS.FETCH](state: any, projectUsage: ProjectUsage) {
state.projectUsage = projectUsage;
},
[PROJECT_USAGE_MUTATIONS.CLEAR](state:any) {
state.projectUsage = {storage: 0, egress: 0, objectsCount: 0} as ProjectUsage;
}
},
actions: {
[PROJECT_USAGE_ACTIONS.FETCH]: async function({commit, rootGetters}: any, {startDate, endDate}: any): Promise<RequestResponse<ProjectUsage>> {
const projectID = rootGetters.selectedProject.id;
let result = await fetchProjectUsage(projectID, startDate, endDate);
if (result.isSuccess) {
commit(PROJECT_USAGE_MUTATIONS.FETCH, result.data);
}
return result;
},
[PROJECT_USAGE_ACTIONS.CLEAR]: function({commit}): void {
commit(PROJECT_USAGE_MUTATIONS.CLEAR);
}
}
};

View File

@ -39,6 +39,11 @@ export const API_KEYS_MUTATIONS = {
CLEAR: 'CLEAR_API_KEYS', CLEAR: 'CLEAR_API_KEYS',
}; };
export const PROJECT_USAGE_MUTATIONS = {
FETCH: 'FETCH_PROJECT_USAGE',
CLEAR: 'CLEAR_PROJECT_USAGE'
};
export const NOTIFICATION_MUTATIONS = { export const NOTIFICATION_MUTATIONS = {
ADD: 'ADD_NOTIFICATION', ADD: 'ADD_NOTIFICATION',
DELETE: 'DELETE_NOTIFICATION', DELETE: 'DELETE_NOTIFICATION',

View File

@ -28,3 +28,12 @@ declare type TeamMemberModel = {
} }
joinedAt: string, joinedAt: string,
}; };
// ProjectUsage sums usage for given period
declare type ProjectUsage = {
storage: number,
egress: number,
objectsCount: number,
since: Date,
before: Date
};

View File

@ -63,3 +63,8 @@ export const API_KEYS_ACTIONS = {
TOGGLE_SELECTION: 'toggleAPIKeySelection', TOGGLE_SELECTION: 'toggleAPIKeySelection',
CLEAR_SELECTION: 'clearAPIKeySelection' CLEAR_SELECTION: 'clearAPIKeySelection'
}; };
export const PROJECT_USAGE_ACTIONS = {
FETCH: 'fetchProjectUsage',
CLEAR: 'clearProjectUsage',
};

View File

@ -26,7 +26,8 @@ import {
PROJETS_ACTIONS, PROJETS_ACTIONS,
PM_ACTIONS, PM_ACTIONS,
USER_ACTIONS, USER_ACTIONS,
API_KEYS_ACTIONS API_KEYS_ACTIONS,
PROJECT_USAGE_ACTIONS
} from '@/utils/constants/actionNames'; } from '@/utils/constants/actionNames';
import ROUTES from '@/utils/constants/routerConstants'; import ROUTES from '@/utils/constants/routerConstants';
import ProjectCreationSuccessPopup from '@/components/project/ProjectCreationSuccessPopup.vue'; import ProjectCreationSuccessPopup from '@/components/project/ProjectCreationSuccessPopup.vue';
@ -66,6 +67,15 @@ import ProjectCreationSuccessPopup from '@/components/project/ProjectCreationSuc
if (!keysResponse.isSuccess) { if (!keysResponse.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch api keys'); this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch api keys');
} }
const currentDate = new Date();
const previousDate = new Date();
previousDate.setMonth(currentDate.getMonth() - 1);
const usageResponse = await this.$store.dispatch(PROJECT_USAGE_ACTIONS.FETCH, {startDate: previousDate, endDate: currentDate});
if (!usageResponse.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch project usage');
}
}, },
components: { components: {
ProjectCreationSuccessPopup, ProjectCreationSuccessPopup,