Skip to content

Commit 1bc2288

Browse files
committed
add copilot seat analysis supports
1 parent 7d3740f commit 1bc2288

File tree

7 files changed

+492
-5
lines changed

7 files changed

+492
-5
lines changed

src/api/ExtractSeats.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// TypeScript
2+
import axios from "axios";
3+
import { Seat } from "../model/Seat";
4+
5+
import organizationMockedResponse_seats from '../assets/organization_response_sample_seats.json';
6+
import enterpriseMockedResponse_seats from '../assets/enterprise_response_sample_seats.json';
7+
8+
export const getSeatsApi = async (): Promise<Seat[]> => {
9+
const perPage = 50;
10+
let page = 1;
11+
let seatsData: Seat[] = [];
12+
13+
let response;
14+
if (process.env.VUE_APP_MOCKED_DATA === "true") {
15+
if (process.env.VUE_APP_SCOPE === "organization") {
16+
response = organizationMockedResponse_seats;
17+
} else if (process.env.VUE_APP_SCOPE === "enterprise") {
18+
response = enterpriseMockedResponse_seats;
19+
} else {
20+
throw new Error(`Invalid VUE_APP_SCOPE value: ${process.env.VUE_APP_SCOPE}. Expected "organization" or "enterprise".`);
21+
}
22+
seatsData = response.seats.map((item: any) => new Seat(item));
23+
} else {
24+
// Fetch the first page
25+
response = await axios.get(`https://api.github.com/orgs/${process.env.VUE_APP_GITHUB_ORG}/copilot/billing/seats`, {
26+
headers: {
27+
Accept: "application/vnd.github+json",
28+
Authorization: `Bearer ${process.env.VUE_APP_GITHUB_TOKEN}`,
29+
"X-GitHub-Api-Version": "2022-11-28",
30+
},
31+
params: {
32+
per_page: perPage,
33+
page: page
34+
}
35+
});
36+
37+
38+
39+
seatsData = seatsData.concat(response.data.seats.map((item: any) => new Seat(item)));
40+
41+
42+
43+
// Calculate the total pages
44+
const totalSeats = response.data.total_seats;
45+
const totalPages = Math.ceil(totalSeats / perPage);
46+
47+
// Fetch the remaining pages
48+
for (page = 2; page <= totalPages; page++) {
49+
response = await axios.get(`https://api.github.com/orgs/${process.env.VUE_APP_GITHUB_ORG}/copilot/billing/seats`, {
50+
headers: {
51+
Accept: "application/vnd.github+json",
52+
Authorization: `Bearer ${process.env.VUE_APP_GITHUB_TOKEN}`,
53+
"X-GitHub-Api-Version": "2022-11-28",
54+
},
55+
params: {
56+
per_page: perPage,
57+
page: page
58+
}
59+
});
60+
61+
seatsData = seatsData.concat(response.data.seats.map((item: any) => new Seat(item)));
62+
}
63+
}
64+
65+
return seatsData;
66+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"total_seats": 2,
3+
"seats": [
4+
{
5+
"created_at": "2021-08-03T18:00:00-06:00",
6+
"updated_at": "2021-09-23T15:00:00-06:00",
7+
"pending_cancellation_date": null,
8+
"last_activity_at": "2021-10-14T00:53:32-06:00",
9+
"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
10+
"assignee": {
11+
"login": "octocat",
12+
"id": 1,
13+
"node_id": "MDQ6VXNlcjE=",
14+
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
15+
"gravatar_id": "",
16+
"url": "https://api.github.com/users/octocat",
17+
"html_url": "https://github.com/octocat",
18+
"followers_url": "https://api.github.com/users/octocat/followers",
19+
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
20+
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
21+
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
22+
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
23+
"organizations_url": "https://api.github.com/users/octocat/orgs",
24+
"repos_url": "https://api.github.com/users/octocat/repos",
25+
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
26+
"received_events_url": "https://api.github.com/users/octocat/received_events",
27+
"type": "User",
28+
"site_admin": false
29+
},
30+
"assigning_team": {
31+
"id": 1,
32+
"node_id": "MDQ6VGVhbTE=",
33+
"url": "https://api.github.com/teams/1",
34+
"html_url": "https://github.com/orgs/github/teams/justice-league",
35+
"name": "Justice League",
36+
"slug": "justice-league",
37+
"description": "A great team.",
38+
"privacy": "closed",
39+
"notification_setting": "notifications_enabled",
40+
"permission": "admin",
41+
"members_url": "https://api.github.com/teams/1/members{/member}",
42+
"repositories_url": "https://api.github.com/teams/1/repos",
43+
"parent": null
44+
}
45+
},
46+
{
47+
"created_at": "2021-09-23T18:00:00-06:00",
48+
"updated_at": "2021-09-23T15:00:00-06:00",
49+
"pending_cancellation_date": "2021-11-01",
50+
"last_activity_at": "2021-10-13T00:53:32-06:00",
51+
"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
52+
"assignee": {
53+
"login": "octokitten",
54+
"id": 1,
55+
"node_id": "MDQ76VNlcjE=",
56+
"avatar_url": "https://github.com/images/error/octokitten_happy.gif",
57+
"gravatar_id": "",
58+
"url": "https://api.github.com/users/octokitten",
59+
"html_url": "https://github.com/octokitten",
60+
"followers_url": "https://api.github.com/users/octokitten/followers",
61+
"following_url": "https://api.github.com/users/octokitten/following{/other_user}",
62+
"gists_url": "https://api.github.com/users/octokitten/gists{/gist_id}",
63+
"starred_url": "https://api.github.com/users/octokitten/starred{/owner}{/repo}",
64+
"subscriptions_url": "https://api.github.com/users/octokitten/subscriptions",
65+
"organizations_url": "https://api.github.com/users/octokitten/orgs",
66+
"repos_url": "https://api.github.com/users/octokitten/repos",
67+
"events_url": "https://api.github.com/users/octokitten/events{/privacy}",
68+
"received_events_url": "https://api.github.com/users/octokitten/received_events",
69+
"type": "User",
70+
"site_admin": false
71+
}
72+
}
73+
]
74+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"total_seats": 2,
3+
"seats": [
4+
{
5+
"created_at": "2021-08-03T18:00:00-06:00",
6+
"updated_at": "2021-09-23T15:00:00-06:00",
7+
"pending_cancellation_date": null,
8+
"last_activity_at": "2021-10-14T00:53:32-06:00",
9+
"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
10+
"assignee": {
11+
"login": "octocat",
12+
"id": 1,
13+
"node_id": "MDQ6VXNlcjE=",
14+
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
15+
"gravatar_id": "",
16+
"url": "https://api.github.com/users/octocat",
17+
"html_url": "https://github.com/octocat",
18+
"followers_url": "https://api.github.com/users/octocat/followers",
19+
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
20+
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
21+
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
22+
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
23+
"organizations_url": "https://api.github.com/users/octocat/orgs",
24+
"repos_url": "https://api.github.com/users/octocat/repos",
25+
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
26+
"received_events_url": "https://api.github.com/users/octocat/received_events",
27+
"type": "User",
28+
"site_admin": false
29+
},
30+
"assigning_team": {
31+
"id": 1,
32+
"node_id": "MDQ6VGVhbTE=",
33+
"url": "https://api.github.com/teams/1",
34+
"html_url": "https://github.com/orgs/github/teams/justice-league",
35+
"name": "Justice League",
36+
"slug": "justice-league",
37+
"description": "A great team.",
38+
"privacy": "closed",
39+
"notification_setting": "notifications_enabled",
40+
"permission": "admin",
41+
"members_url": "https://api.github.com/teams/1/members{/member}",
42+
"repositories_url": "https://api.github.com/teams/1/repos",
43+
"parent": null
44+
}
45+
},
46+
{
47+
"created_at": "2021-09-23T18:00:00-06:00",
48+
"updated_at": "2021-09-23T15:00:00-06:00",
49+
"pending_cancellation_date": "2021-11-01",
50+
"last_activity_at": "2021-10-13T00:53:32-06:00",
51+
"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
52+
"assignee": {
53+
"login": "octokitten",
54+
"id": 1,
55+
"node_id": "MDQ76VNlcjE=",
56+
"avatar_url": "https://github.com/images/error/octokitten_happy.gif",
57+
"gravatar_id": "",
58+
"url": "https://api.github.com/users/octokitten",
59+
"html_url": "https://github.com/octokitten",
60+
"followers_url": "https://api.github.com/users/octokitten/followers",
61+
"following_url": "https://api.github.com/users/octokitten/following{/other_user}",
62+
"gists_url": "https://api.github.com/users/octokitten/gists{/gist_id}",
63+
"starred_url": "https://api.github.com/users/octokitten/starred{/owner}{/repo}",
64+
"subscriptions_url": "https://api.github.com/users/octokitten/subscriptions",
65+
"organizations_url": "https://api.github.com/users/octokitten/orgs",
66+
"repos_url": "https://api.github.com/users/octokitten/repos",
67+
"events_url": "https://api.github.com/users/octokitten/events{/privacy}",
68+
"received_events_url": "https://api.github.com/users/octokitten/received_events",
69+
"type": "User",
70+
"site_admin": false
71+
}
72+
}
73+
]
74+
}

src/components/ApiResponse.vue

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111
<div v-if="showCopyMessage" :class="{'copy-message': true, 'error': isError}">{{ message }}</div>
1212
</transition>
1313
</div>
14+
15+
<v-card max-height="575px" class="overflow-y-auto">
16+
<pre ref="jsonText">{{ JSON.stringify(seats, null, 2) }}</pre>
17+
</v-card>
18+
<div class="copy-container">
19+
<v-btn @click="showSeatCount">Show Assigned Seats count</v-btn>
20+
<transition name="fade">
21+
<div v-if="showCopyMessage" :class="{'copy-message': true, 'error': isError}">{{ message }}</div>
22+
</transition>
23+
</div>
1424
</v-container>
1525
</template>
1626

@@ -23,6 +33,10 @@ export default defineComponent({
2333
metrics: {
2434
type: Object,
2535
required: true
36+
},
37+
seats: {
38+
type: Array,
39+
required: true
2640
}
2741
},
2842
data() {
@@ -51,7 +65,19 @@ export default defineComponent({
5165
setTimeout(() => {
5266
this.showCopyMessage = false;
5367
}, 3000);
68+
},
69+
70+
showSeatCount() {
71+
const seatCount = this.seats.length;
72+
console.log('Seat count:', seatCount);
73+
this.message = `Seat count: ${seatCount}`;
74+
75+
this.showCopyMessage = true;
76+
setTimeout(() => {
77+
this.showCopyMessage = false;
78+
}, 3000);
5479
}
80+
5581
}
5682
5783
});

src/components/MainComponent.vue

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
<BreakdownComponent v-if="item === 'languages'" :metrics="metrics" :breakdownKey="'language'"/>
3232
<BreakdownComponent v-if="item === 'editors'" :metrics="metrics" :breakdownKey="'editor'"/>
3333
<CopilotChatViewer v-if="item === 'copilot chat'" :metrics="metrics" />
34-
<ApiResponse v-if="item === 'api response'" :metrics="metrics" />
34+
<SeatsAnalysisViewer v-if="item === 'seat analysis'" :seats="seats" />
35+
<ApiResponse v-if="item === 'api response'" :metrics="metrics" :seats="seats" />
3536
</v-card>
3637
</v-window-item>
3738
</v-window>
@@ -44,12 +45,15 @@
4445
<script lang='ts'>
4546
import { defineComponent, ref } from 'vue'
4647
import { getMetricsApi } from '../api/GitHubApi';
48+
import { getSeatsApi } from '../api/ExtractSeats';
4749
import { Metrics } from '../model/Metrics';
50+
import { Seat } from "../model/Seat";
4851
4952
//Components
5053
import MetricsViewer from './MetricsViewer.vue'
5154
import BreakdownComponent from './BreakdownComponent.vue'
5255
import CopilotChatViewer from './CopilotChatViewer.vue'
56+
import SeatsAnalysisViewer from './SeatsAnalysisViewer.vue'
5357
import ApiResponse from './ApiResponse.vue'
5458
5559
export default defineComponent({
@@ -58,6 +62,7 @@ export default defineComponent({
5862
MetricsViewer,
5963
BreakdownComponent,
6064
CopilotChatViewer,
65+
SeatsAnalysisViewer,
6166
ApiResponse
6267
},
6368
computed: {
@@ -78,7 +83,7 @@ export default defineComponent({
7883
},
7984
data () {
8085
return {
81-
tabItems: ['languages', 'editors', 'copilot chat', 'api response'],
86+
tabItems: ['languages', 'editors', 'copilot chat','seat analysis', 'api response'],
8287
tab: null
8388
}
8489
},
@@ -90,7 +95,8 @@ export default defineComponent({
9095
setup() {
9196
const metricsReady = ref(false);
9297
const metrics = ref<Metrics[]>([]);
93-
98+
const seatsReady = ref(false); // Add this line by zhuang to the setup function
99+
const seats = ref<Seat[]>([]); // Add this line by zhuang to the setup function
94100
// API Error Message
95101
const apiError = ref<string | undefined>(undefined);
96102
@@ -123,8 +129,38 @@ export default defineComponent({
123129
apiError.value += ' <br> If .env file is modified, restart the app for the changes to take effect.';
124130
125131
});
126-
127-
return { metricsReady, metrics, apiError };
132+
133+
getSeatsApi().then(data => {
134+
seats.value = data;
135+
136+
// Set seatsReady to true after the call completes.
137+
seatsReady.value = true;
138+
139+
}).catch(error => {
140+
console.log(error);
141+
// Check the status code of the error response
142+
if (error.response && error.response.status) {
143+
switch (error.response.status) {
144+
case 401:
145+
apiError.value = '401 Unauthorized access - check if your token in the .env file is correct.';
146+
break;
147+
case 404:
148+
apiError.value = `404 Not Found - is the organization '${process.env.VUE_APP_GITHUB_ORG}' correct?`;
149+
break;
150+
default:
151+
apiError.value = error.message;
152+
break;
153+
}
154+
} else {
155+
// Update apiError with the error message
156+
apiError.value = error.message;
157+
}
158+
// Add a new line to the apiError message
159+
apiError.value += ' <br> If .env file is modified, restart the app for the changes to take effect.';
160+
161+
});
162+
163+
return { metricsReady, metrics, seatsReady,seats,apiError };
128164
}
129165
})
130166
</script>

0 commit comments

Comments
 (0)