Skip to content

Commit f93895f

Browse files
bujjibabukattaBujjibabukattaewega
authored
fix(gh-copilot): preserve hyphenated enterprise slugs (apache#8935)
Co-authored-by: Bujjibabukatta <bujjubabukatta6@gmail.com> Co-authored-by: Eldrick Wega <eldrick.wega@outlook.com> Co-authored-by: Eldrick Wega <ewega@github.com>
1 parent 52278b0 commit f93895f

12 files changed

Lines changed: 133 additions & 21 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package service
19+
20+
import (
21+
"fmt"
22+
"net/url"
23+
"strings"
24+
)
25+
26+
func copilotAPIPath(namespace, slug, resource string) string {
27+
return fmt.Sprintf("%s/%s/%s", namespace, url.PathEscape(strings.TrimSpace(slug)), strings.TrimPrefix(resource, "/"))
28+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package service
19+
20+
import (
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
)
25+
26+
func TestCopilotAPIPathPreservesHyphenatedEnterpriseSlug(t *testing.T) {
27+
path := copilotAPIPath("enterprises", "my-enterprise", "copilot/billing/seats")
28+
29+
assert.Equal(t, "enterprises/my-enterprise/copilot/billing/seats", path)
30+
}

backend/plugins/gh-copilot/service/connection_test_helper.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func TestConnection(ctx stdctx.Context, br corectx.BasicRes, connection *models.
8585
// Note: /enterprises/{ent}/copilot/billing does not exist — use /billing/seats instead.
8686
if hasEnterprise {
8787
entSlug := strings.TrimSpace(connection.Enterprise)
88-
seatsPath := fmt.Sprintf("enterprises/%s/copilot/billing/seats", entSlug)
88+
seatsPath := copilotAPIPath("enterprises", entSlug, "copilot/billing/seats")
8989
entSummary, entErr := fetchSeatsSummary(apiClient, seatsPath)
9090
if entErr != nil {
9191
return nil, entErr
@@ -97,7 +97,7 @@ func TestConnection(ctx stdctx.Context, br corectx.BasicRes, connection *models.
9797

9898
// Test org endpoint when configured.
9999
if hasOrg {
100-
orgSummary, orgErr := fetchBillingSummary(apiClient, fmt.Sprintf("orgs/%s/copilot/billing", connection.Organization))
100+
orgSummary, orgErr := fetchBillingSummary(apiClient, copilotAPIPath("orgs", connection.Organization, "copilot/billing"))
101101
if orgErr != nil {
102102
return nil, orgErr
103103
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package tasks
19+
20+
import (
21+
"fmt"
22+
"net/url"
23+
"strings"
24+
)
25+
26+
func copilotAPIPath(namespace, slug, resource string) string {
27+
return fmt.Sprintf("%s/%s/%s", namespace, url.PathEscape(strings.TrimSpace(slug)), strings.TrimPrefix(resource, "/"))
28+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package tasks
19+
20+
import (
21+
"testing"
22+
23+
"github.com/stretchr/testify/require"
24+
)
25+
26+
func TestCopilotAPIPathPreservesHyphenatedEnterpriseSlug(t *testing.T) {
27+
path := copilotAPIPath("enterprises", "my-enterprise", "copilot/billing/seats")
28+
29+
require.Equal(t, "enterprises/my-enterprise/copilot/billing/seats", path)
30+
}

backend/plugins/gh-copilot/tasks/enterprise_metrics_collector.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package tasks
1919

2020
import (
2121
"encoding/json"
22-
"fmt"
2322
"net/http"
2423
"net/url"
2524
"time"
@@ -81,10 +80,9 @@ func CollectEnterpriseMetrics(taskCtx plugin.SubTaskContext) errors.Error {
8180
dayIter := newDayIterator(start, until)
8281

8382
err = collector.InitCollector(helper.ApiCollectorArgs{
84-
ApiClient: apiClient,
85-
Input: dayIter,
86-
UrlTemplate: fmt.Sprintf("enterprises/%s/copilot/metrics/reports/enterprise-1-day",
87-
connection.Enterprise),
83+
ApiClient: apiClient,
84+
Input: dayIter,
85+
UrlTemplate: copilotAPIPath("enterprises", connection.Enterprise, "copilot/metrics/reports/enterprise-1-day"),
8886
Query: func(reqData *helper.RequestData) (url.Values, errors.Error) {
8987
input := reqData.Input.(*dayInput)
9088
q := url.Values{}

backend/plugins/gh-copilot/tasks/org_metrics_collector.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package tasks
1919

2020
import (
2121
"encoding/json"
22-
"fmt"
2322
"io"
2423
"net/http"
2524
"net/url"
@@ -76,10 +75,9 @@ func CollectOrgMetrics(taskCtx plugin.SubTaskContext) errors.Error {
7675
dayIter := newDayIterator(start, until)
7776

7877
err = collector.InitCollector(helper.ApiCollectorArgs{
79-
ApiClient: apiClient,
80-
Input: dayIter,
81-
UrlTemplate: fmt.Sprintf("orgs/%s/copilot/metrics/reports/organization-1-day",
82-
connection.Organization),
78+
ApiClient: apiClient,
79+
Input: dayIter,
80+
UrlTemplate: copilotAPIPath("orgs", connection.Organization, "copilot/metrics/reports/organization-1-day"),
8381
Query: func(reqData *helper.RequestData) (url.Values, errors.Error) {
8482
input := reqData.Input.(*dayInput)
8583
q := url.Values{}

backend/plugins/gh-copilot/tasks/seat_collector.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ func CollectCopilotSeatAssignments(taskCtx plugin.SubTaskContext) errors.Error {
9090
var urlTemplate string
9191
switch {
9292
case connection.HasEnterprise():
93-
urlTemplate = fmt.Sprintf("enterprises/%s/copilot/billing/seats", connection.Enterprise)
93+
urlTemplate = copilotAPIPath("enterprises", connection.Enterprise, "copilot/billing/seats")
9494
case connection.Organization != "":
95-
urlTemplate = fmt.Sprintf("orgs/%s/copilot/billing/seats", connection.Organization)
95+
urlTemplate = copilotAPIPath("orgs", connection.Organization, "copilot/billing/seats")
9696
default:
9797
taskCtx.GetLogger().Warn(nil, "skipping seat collection: no enterprise or organization configured on connection %d", connection.ID)
9898
return nil

backend/plugins/gh-copilot/tasks/user_metrics_collector.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package tasks
1919

2020
import (
2121
"encoding/json"
22-
"fmt"
2322
"io"
2423
"net/http"
2524
"net/url"
@@ -95,9 +94,9 @@ func CollectUserMetrics(taskCtx plugin.SubTaskContext) errors.Error {
9594
var urlTemplate string
9695

9796
if connection.HasEnterprise() {
98-
urlTemplate = fmt.Sprintf("enterprises/%s/copilot/metrics/reports/users-1-day", connection.Enterprise)
97+
urlTemplate = copilotAPIPath("enterprises", connection.Enterprise, "copilot/metrics/reports/users-1-day")
9998
} else if connection.Organization != "" {
100-
urlTemplate = fmt.Sprintf("orgs/%s/copilot/metrics/reports/users-1-day", connection.Organization)
99+
urlTemplate = copilotAPIPath("orgs", connection.Organization, "copilot/metrics/reports/users-1-day")
101100
} else {
102101
return nil
103102
}

backend/plugins/gh-copilot/tasks/user_teams_collector.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package tasks
1919

2020
import (
2121
"encoding/json"
22-
"fmt"
2322
"io"
2423
"net/http"
2524
"net/url"
@@ -45,9 +44,9 @@ func CollectUserTeams(taskCtx plugin.SubTaskContext) errors.Error {
4544
var urlTemplate string
4645

4746
if connection.HasEnterprise() {
48-
urlTemplate = fmt.Sprintf("enterprises/%s/copilot/metrics/reports/user-teams-1-day", connection.Enterprise)
47+
urlTemplate = copilotAPIPath("enterprises", connection.Enterprise, "copilot/metrics/reports/user-teams-1-day")
4948
} else if connection.Organization != "" {
50-
urlTemplate = fmt.Sprintf("orgs/%s/copilot/metrics/reports/user-teams-1-day", connection.Organization)
49+
urlTemplate = copilotAPIPath("orgs", connection.Organization, "copilot/metrics/reports/user-teams-1-day")
5150
} else {
5251
return nil
5352
}

0 commit comments

Comments
 (0)