Skip to content

Commit 5072f71

Browse files
authored
feat: add version check util function (#244)
1 parent 66486d9 commit 5072f71

File tree

4 files changed

+259
-0
lines changed

4 files changed

+259
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- update sync to 0.12.0
1616
- update test to 0.23.0
1717

18+
### Added
19+
20+
- new function to check remote console version to enable/disable functionality or feature of the cli
21+
1822
## [v0.17.2] - 2025-03-04
1923

2024
### Changed

internal/resources/responses.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ type APIError struct {
2828
Message string `json:"message"`
2929
}
3030

31+
type Version struct {
32+
Version string `json:"version"`
33+
Major string `json:"major"`
34+
Minor string `json:"minor"`
35+
}
36+
3137
type AuthProvider struct {
3238
ID string `json:"id"`
3339
Label string `json:"label"`

internal/util/version_check.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright Mia srl
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package util
17+
18+
import (
19+
"context"
20+
"fmt"
21+
"net/http"
22+
"strconv"
23+
24+
"github.com/mia-platform/miactl/internal/client"
25+
"github.com/mia-platform/miactl/internal/resources"
26+
)
27+
28+
const (
29+
defaultMajor = "13"
30+
defaultMinor = "6"
31+
)
32+
33+
var cachedVersions = map[string]*resources.Version{}
34+
35+
// VersionCheck will check if the remote version endpoint is greater or not of major and minor version passed
36+
func VersionCheck(ctx context.Context, client client.Interface, major, minor int) (bool, error) {
37+
remoteMajor, remoteMinor, err := remoteVersion(ctx, client)
38+
if err != nil {
39+
return false, err
40+
}
41+
42+
return versionCompare(remoteMajor, remoteMinor, major, minor), nil
43+
}
44+
45+
func remoteVersion(ctx context.Context, client client.Interface) (string, string, error) {
46+
request := client.Get().APIPath("/api/version")
47+
cacheKey := request.URL().String()
48+
if version, found := getFromCache(request.URL().String()); found {
49+
return version.Major, version.Minor, nil
50+
}
51+
52+
response, err := request.Do(ctx)
53+
switch {
54+
case err != nil:
55+
return "", "", err
56+
case response.StatusCode() != http.StatusNotFound && response.StatusCode() != http.StatusOK:
57+
return "", "", response.Error()
58+
case response.StatusCode() == http.StatusNotFound:
59+
return defaultMajor, defaultMinor, nil
60+
}
61+
62+
var version = new(resources.Version)
63+
err = response.ParseResponse(version)
64+
if err != nil {
65+
return "", "", fmt.Errorf("failed to parse version response: %w", err)
66+
}
67+
68+
saveInCache(cacheKey, version)
69+
return version.Major, version.Minor, nil
70+
}
71+
72+
func getFromCache(key string) (*resources.Version, bool) {
73+
version, found := cachedVersions[key]
74+
return version, found
75+
}
76+
77+
func saveInCache(key string, version *resources.Version) {
78+
cachedVersions[key] = version
79+
}
80+
81+
func versionCompare(major, minor string, compareMajor, compareMinor int) bool {
82+
majorInt, _ := strconv.Atoi(major)
83+
84+
switch {
85+
case majorInt < compareMajor:
86+
return false
87+
case majorInt > compareMajor:
88+
return true
89+
}
90+
91+
minorInt, _ := strconv.Atoi(minor)
92+
return minorInt >= compareMinor
93+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// Copyright Mia srl
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package util
17+
18+
import (
19+
"context"
20+
"net/http"
21+
"net/http/httptest"
22+
"strconv"
23+
"testing"
24+
"time"
25+
26+
"github.com/mia-platform/miactl/internal/client"
27+
"github.com/stretchr/testify/assert"
28+
"github.com/stretchr/testify/require"
29+
)
30+
31+
func TestCompareVersion(t *testing.T) {
32+
t.Parallel()
33+
34+
defaultMajorInt, err := strconv.Atoi(defaultMajor)
35+
require.NoError(t, err)
36+
defaultMinorInt, err := strconv.Atoi(defaultMinor)
37+
require.NoError(t, err)
38+
39+
testCases := map[string]struct {
40+
major int
41+
minor int
42+
check bool
43+
testServer *httptest.Server
44+
expectedError string
45+
}{
46+
"missing api use default version - check true minor": {
47+
major: defaultMajorInt,
48+
minor: defaultMinorInt,
49+
check: true,
50+
testServer: versionTestServer(t, func(w http.ResponseWriter) {
51+
w.WriteHeader(http.StatusNotFound)
52+
}),
53+
},
54+
"missing api use default version - check true major": {
55+
major: defaultMajorInt - 1,
56+
minor: defaultMinorInt + 1,
57+
check: true,
58+
testServer: versionTestServer(t, func(w http.ResponseWriter) {
59+
w.WriteHeader(http.StatusNotFound)
60+
}),
61+
},
62+
"missing api use default version - check false major": {
63+
major: defaultMajorInt + 1,
64+
minor: defaultMinorInt - 1,
65+
check: false,
66+
testServer: versionTestServer(t, func(w http.ResponseWriter) {
67+
w.WriteHeader(http.StatusNotFound)
68+
}),
69+
},
70+
"missing api use default version - check false minor": {
71+
major: defaultMajorInt,
72+
minor: defaultMinorInt + 1,
73+
check: false,
74+
testServer: versionTestServer(t, func(w http.ResponseWriter) {
75+
w.WriteHeader(http.StatusNotFound)
76+
}),
77+
},
78+
"other error return error": {
79+
testServer: versionTestServer(t, func(w http.ResponseWriter) {
80+
w.WriteHeader(http.StatusInternalServerError)
81+
w.Write([]byte(`{"statusCode": 500, "message": "error from server"}`))
82+
}),
83+
expectedError: "error from server",
84+
},
85+
"successful get return a valid version - check true minor": {
86+
testServer: versionTestServer(t, func(w http.ResponseWriter) {
87+
w.Write([]byte(`{"major": "5", "minor":"11"}`))
88+
}),
89+
major: 5,
90+
minor: 10,
91+
check: true,
92+
},
93+
"successful get return a valid version - check true major": {
94+
testServer: versionTestServer(t, func(w http.ResponseWriter) {
95+
w.Write([]byte(`{"major": "5", "minor":"11"}`))
96+
}),
97+
major: 4,
98+
minor: 12,
99+
check: true,
100+
},
101+
"successful get return a valid version - check false major": {
102+
testServer: versionTestServer(t, func(w http.ResponseWriter) {
103+
w.Write([]byte(`{"major": "5", "minor":"10"}`))
104+
}),
105+
major: 6,
106+
minor: 10,
107+
check: false,
108+
},
109+
"successful get return a valid version - check false minor": {
110+
testServer: versionTestServer(t, func(w http.ResponseWriter) {
111+
w.Write([]byte(`{"major": "5", "minor":"10"}`))
112+
}),
113+
major: 5,
114+
minor: 11,
115+
check: false,
116+
},
117+
}
118+
119+
for name, test := range testCases {
120+
t.Run(name, func(t *testing.T) {
121+
server := test.testServer
122+
defer server.Close()
123+
124+
client, err := client.APIClientForConfig(&client.Config{
125+
Host: server.URL,
126+
})
127+
require.NoError(t, err)
128+
ctx, cancel := context.WithTimeout(t.Context(), 1*time.Second)
129+
defer cancel()
130+
131+
check, err := VersionCheck(ctx, client, test.major, test.minor)
132+
if len(test.expectedError) > 0 {
133+
assert.Error(t, err)
134+
assert.False(t, check)
135+
return
136+
}
137+
138+
assert.NoError(t, err)
139+
assert.Equal(t, test.check, check)
140+
})
141+
}
142+
}
143+
144+
func versionTestServer(t *testing.T, response func(w http.ResponseWriter)) *httptest.Server {
145+
t.Helper()
146+
147+
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
148+
switch {
149+
default:
150+
w.WriteHeader(http.StatusNotFound)
151+
assert.Fail(t, "request not expexted")
152+
case r.URL.Path == "/api/version" && r.Method == http.MethodGet:
153+
response(w)
154+
}
155+
}))
156+
}

0 commit comments

Comments
 (0)