Skip to content

Commit ec1e6d9

Browse files
committed
refactor: Centralize configuration
1 parent 8818f0f commit ec1e6d9

File tree

7 files changed

+122
-73
lines changed

7 files changed

+122
-73
lines changed

public/assets/app-config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
window._ENV_ = {
2+
// These values are replaced by the entrypoint of Docker Image
3+
VUE_APP_MOCKED_DATA: "${VUE_APP_MOCKED_DATA}",
4+
VUE_APP_SCOPE: "${VUE_APP_SCOPE}",
5+
VUE_APP_GITHUB_ORG: "${VUE_APP_GITHUB_ORG}",
6+
VUE_APP_GITHUB_ENT: "${VUE_APP_GITHUB_ENT}",
7+
VUE_APP_GITHUB_TOKEN: "${VUE_APP_GITHUB_TOKEN}",
8+
};

public/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<meta name="viewport" content="width=device-width,initial-scale=1.0">
77
<link rel="icon" href="<%= BASE_URL %>favicon.svg">
88
<title><%= htmlWebpackPlugin.options.title %></title>
9+
<script src="<%= BASE_URL %>assets/app-config.js"></script>
910
</head>
1011
<body>
1112
<noscript>

src/api/ExtractSeats.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// TypeScript
22
import axios from "axios";
33
import { Seat } from "../model/Seat";
4+
import config from '../config';
45

56
import organizationMockedResponse_seats from '../assets/organization_response_sample_seats.json';
67
import enterpriseMockedResponse_seats from '../assets/enterprise_response_sample_seats.json';
@@ -11,21 +12,21 @@ export const getSeatsApi = async (): Promise<Seat[]> => {
1112
let seatsData: Seat[] = [];
1213

1314
let response;
14-
if (process.env.VUE_APP_SCOPE !== "organization") {
15+
if (config.scope.type !== "organization") {
1516
// when the scope is not organization, return seatsData,by default it will return empty array
1617
return seatsData;
1718
}
18-
else{
19-
if (process.env.VUE_APP_MOCKED_DATA === "true") {
19+
else {
20+
if (config.mockedData) {
2021
response = organizationMockedResponse_seats;
2122
seatsData = seatsData.concat(response.seats.map((item: any) => new Seat(item)));
22-
}
23-
else if (process.env.VUE_APP_MOCKED_DATA === "false") {
24-
// Fetch the first page to get the total number of seats
25-
response = await axios.get(`https://api.github.com/orgs/${process.env.VUE_APP_GITHUB_ORG}/copilot/billing/seats`, {
23+
}
24+
else {
25+
// Fetch the first page to get the total number of seats
26+
response = await axios.get(`${config.github.apiUrl}/copilot/billing/seats`, {
2627
headers: {
2728
Accept: "application/vnd.github+json",
28-
Authorization: `Bearer ${process.env.VUE_APP_GITHUB_TOKEN}`,
29+
Authorization: `Bearer ${config.github.token}`,
2930
"X-GitHub-Api-Version": "2022-11-28",
3031
},
3132
params: {
@@ -41,10 +42,10 @@ export const getSeatsApi = async (): Promise<Seat[]> => {
4142

4243
// Fetch the remaining pages
4344
for (page = 2; page <= totalPages; page++) {
44-
response = await axios.get(`https://api.github.com/orgs/${process.env.VUE_APP_GITHUB_ORG}/copilot/billing/seats`, {
45+
response = await axios.get(`${config.github.apiUrl}/copilot/billing/seats`, {
4546
headers: {
4647
Accept: "application/vnd.github+json",
47-
Authorization: `Bearer ${process.env.VUE_APP_GITHUB_TOKEN}`,
48+
Authorization: `Bearer ${config.github.token}`,
4849
"X-GitHub-Api-Version": "2022-11-28",
4950
},
5051
params: {
@@ -54,8 +55,8 @@ export const getSeatsApi = async (): Promise<Seat[]> => {
5455
});
5556

5657
seatsData = seatsData.concat(response.data.seats.map((item: any) => new Seat(item)));
57-
} //end of else if (process.env.VUE_APP_MOCKED_DATA === "false")
58-
} //end of else if (process.env.VUE_APP_SCOPE !== "organization")
59-
return seatsData;
60-
}
58+
}
59+
}
60+
return seatsData;
61+
}
6162
}

src/api/GitHubApi.ts

Lines changed: 17 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,66 +9,40 @@ import axios from "axios";
99
import { Metrics } from "../model/Metrics";
1010
import organizationMockedResponse from '../assets/organization_response_sample.json';
1111
import enterpriseMockedResponse from '../assets/enterprise_response_sample.json';
12-
12+
import config from '../config';
1313

1414
export const getMetricsApi = async (): Promise<Metrics[]> => {
15-
15+
1616
let response;
1717
let metricsData;
1818

19-
if (process.env.VUE_APP_MOCKED_DATA === "true") {
19+
if (config.mockedData) {
2020
console.log("Using mock data. Check VUE_APP_MOCKED_DATA variable.");
21-
if (process.env.VUE_APP_SCOPE === "organization") {
22-
response = organizationMockedResponse;
23-
} else if (process.env.VUE_APP_SCOPE === "enterprise") {
24-
response = enterpriseMockedResponse;
25-
} else {
26-
throw new Error(`Invalid VUE_APP_SCOPE value: ${process.env.VUE_APP_SCOPE}. Expected "organization" or "enterprise".`);
27-
}
28-
21+
response = config.scope.type === "organization" ? organizationMockedResponse : enterpriseMockedResponse;
2922
metricsData = response.map((item: any) => new Metrics(item));
3023
} else {
31-
// if VUE_APP_GITHUB_TOKEN is not set, throw an error
32-
if (!process.env.VUE_APP_GITHUB_TOKEN) {
33-
throw new Error("VUE_APP_GITHUB_TOKEN environment variable is not set.");
34-
}
35-
if (process.env.VUE_APP_SCOPE === "organization") {
36-
response = await axios.get(
37-
`https://api.github.com/orgs/${process.env.VUE_APP_GITHUB_ORG}/copilot/usage`,
38-
{
39-
headers: {
40-
Accept: "application/vnd.github+json",
41-
Authorization: `Bearer ${process.env.VUE_APP_GITHUB_TOKEN}`,
42-
"X-GitHub-Api-Version": "2022-11-28",
43-
},
44-
}
45-
);
46-
} else if (process.env.VUE_APP_SCOPE === "enterprise") {
24+
response = await axios.get(
25+
`${config.github.apiUrl}/copilot/usage`,
26+
{
27+
headers: {
28+
Accept: "application/vnd.github+json",
29+
Authorization: `Bearer ${config.github.token}`,
30+
"X-GitHub-Api-Version": "2022-11-28",
31+
},
32+
}
33+
);
4734

48-
response = await axios.get(
49-
`https://api.github.com/enterprises/${process.env.VUE_APP_GITHUB_ENT}/copilot/usage`,
50-
{
51-
headers: {
52-
Accept: "application/vnd.github+json",
53-
Authorization: `Bearer ${process.env.VUE_APP_GITHUB_TOKEN}`,
54-
"X-GitHub-Api-Version": "2022-11-28",
55-
},
56-
}
57-
);
58-
} else {
59-
throw new Error(`Invalid VUE_APP_SCOPE value: ${process.env.VUE_APP_SCOPE}. Expected "organization" or "enterprise".`);
60-
}
6135

6236
metricsData = response.data.map((item: any) => new Metrics(item));
6337
}
6438
return metricsData;
6539
};
6640

67-
export const getTeams = async (): Promise<string[]> =>{
68-
const response = await axios.get(`https://api.github.com/orgs/${process.env.VUE_APP_GITHUB_ORG}/teams`, {
41+
export const getTeams = async (): Promise<string[]> => {
42+
const response = await axios.get(`${config.github.apiUrl}/teams`, {
6943
headers: {
7044
Accept: 'application/vnd.github+json',
71-
Authorization: `Bearer ${process.env.VUE_APP_GITHUB_TOKEN}`,
45+
Authorization: `Bearer ${config.github.token}`,
7246
'X-GitHub-Api-Version': '2022-11-28',
7347
},
7448
});

src/components/ApiResponse.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
<script lang="ts">
3232
import { defineComponent } from 'vue';
33+
import config from '../config';
3334
3435
export default defineComponent({
3536
name: 'ApiResponse',
@@ -45,7 +46,7 @@ export default defineComponent({
4546
},
4647
data() {
4748
return {
48-
vueAppScope: process.env.VUE_APP_SCOPE,
49+
vueAppScope: config.scope.type,
4950
showCopyMessage: false,
5051
showSeatMessage: false,
5152
isError: false,

src/components/MainComponent.vue

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import BreakdownComponent from './BreakdownComponent.vue'
5757
import CopilotChatViewer from './CopilotChatViewer.vue'
5858
import SeatsAnalysisViewer from './SeatsAnalysisViewer.vue'
5959
import ApiResponse from './ApiResponse.vue'
60+
import config from '../config';
6061
6162
export default defineComponent({
6263
name: 'MainComponent',
@@ -69,27 +70,22 @@ export default defineComponent({
6970
},
7071
computed: {
7172
gitHubOrgName() {
72-
return process.env.VUE_APP_GITHUB_ORG;
73+
return config.github.org;
7374
},
7475
itemName() {
75-
if (process.env.VUE_APP_SCOPE === 'enterprise' || process.env.VUE_APP_SCOPE === 'organization') {
76-
return process.env.VUE_APP_SCOPE;
77-
} else {
78-
console.log("invalid");
79-
return 'invalid';
80-
}
76+
return config.scope.type;
8177
},
8278
capitalizedItemName():string {
8379
return this.itemName.charAt(0).toUpperCase() + this.itemName.slice(1);
8480
},
8581
displayedViewName(): string {
86-
return this.capitalizedItemName === 'Enterprise' ? process.env.VUE_APP_GITHUB_ENT: process.env.VUE_APP_GITHUB_ORG;
82+
return config.scope.name;
8783
},
8884
isScopeOrganization() {
89-
return process.env.VUE_APP_SCOPE === 'organization';
85+
return config.scope.type === 'organization';
9086
},
9187
mockedDataMessage() {
92-
return process.env.VUE_APP_MOCKED_DATA === 'true' ? 'Using mock data - see README if unintended' : '';
88+
return config.mockedData ? 'Using mock data - see README if unintended' : '';
9389
}
9490
},
9591
data () {
@@ -99,10 +95,8 @@ export default defineComponent({
9995
}
10096
},
10197
created() {
102-
if(this.itemName !== 'invalid'){
10398
this.tabItems.unshift(this.itemName);
104-
}
105-
if (process.env.VUE_APP_SCOPE === 'organization') {
99+
if (config.scope.type === 'organization') {
106100
// get the last item in the array,which is 'api response'
107101
//and add 'seat analysis' before it
108102
let lastItem = this.tabItems.pop();
@@ -135,7 +129,7 @@ export default defineComponent({
135129
apiError.value = '401 Unauthorized access - check if your token in the .env file is correct.';
136130
break;
137131
case 404:
138-
apiError.value = `404 Not Found - is the organization '${process.env.VUE_APP_GITHUB_ORG}' correct?`;
132+
apiError.value = `404 Not Found - is the ${config.scope.type} '${config.scope.name}' correct?`;
139133
break;
140134
default:
141135
apiError.value = error.message;
@@ -165,7 +159,7 @@ export default defineComponent({
165159
apiError.value = '401 Unauthorized access - check if your token in the .env file is correct.';
166160
break;
167161
case 404:
168-
apiError.value = `404 Not Found - is the organization '${process.env.VUE_APP_GITHUB_ORG}' correct?`;
162+
apiError.value = `404 Not Found - is the ${config.scope.type} '${config.scope.name}' correct?`;
169163
break;
170164
default:
171165
apiError.value = error.message;

src/config.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const PROPS = ["MOCKED_DATA", "SCOPE", "GITHUB_ORG", "GITHUB_ENT", "GITHUB_TOKEN"];
2+
3+
const env: any = {};
4+
PROPS.forEach(prop => {
5+
const propName = `VUE_APP_${prop}`;
6+
if (process.env.NODE_ENV === "production") {
7+
env[propName] = (window as any)["_ENV_"][propName];
8+
}
9+
else {
10+
env[propName] = process.env[propName];
11+
}
12+
});
13+
14+
const VALID_SCOPE = ['organization', 'enterprise'];
15+
16+
let scopeType;
17+
if (VALID_SCOPE.includes(env.VUE_APP_SCOPE)) {
18+
scopeType = env.VUE_APP_SCOPE as 'enterprise' | 'organization'
19+
}
20+
21+
let apiUrl: string;
22+
const githubOrgName = env.VUE_APP_GITHUB_ORG;
23+
const githubEntName = env.VUE_APP_GITHUB_ENT;
24+
25+
let scopeName: string;
26+
if (scopeType === 'organization') {
27+
scopeName = githubOrgName;
28+
apiUrl = `https://api.github.com/orgs/${githubOrgName}`;
29+
}
30+
else if (scopeType === 'enterprise') {
31+
scopeName = githubEntName;
32+
apiUrl = `https://api.github.com/enterprises/${githubEntName}`;
33+
}
34+
else {
35+
throw new Error(`Invalid VUE_APP_SCOPE value: ${env.VUE_APP_SCOPE}. Valid values: ${VALID_SCOPE.join(', ')}`)
36+
}
37+
38+
const config: Config = {
39+
mockedData: env.VUE_APP_MOCKED_DATA === "true",
40+
scope: {
41+
type: scopeType,
42+
name: scopeName
43+
44+
},
45+
github: {
46+
org: githubOrgName,
47+
ent: githubEntName,
48+
token: env.VUE_APP_GITHUB_TOKEN,
49+
apiUrl
50+
}
51+
}
52+
if (!config.mockedData && !config.github.token) {
53+
throw new Error("VUE_APP_GITHUB_TOKEN environment variable must be set.");
54+
}
55+
56+
export default config;
57+
58+
interface Config {
59+
mockedData: boolean;
60+
scope: {
61+
type: 'organization' | 'enterprise';
62+
name: string;
63+
};
64+
github: {
65+
org: string;
66+
ent: string;
67+
token: string;
68+
apiUrl: string;
69+
}
70+
}

0 commit comments

Comments
 (0)