internal/cloud/versions.go (112 lines of code) (raw):

/* * Copyright 2021-2024 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cloud import ( "encoding/json" "errors" "fmt" "net/http" "strconv" "strings" log "github.com/sirupsen/logrus" ) const ( RequiredMajorVersion = 1 MinimumRequiredMinorVersion = 0 VersionsURI = "/api/versions" ) type APIVersion struct { Major int Minor int } type ApiVersionMismatchError struct { ApiKind string SupportedVersions []string } func (e *ApiVersionMismatchError) Error() string { return fmt.Sprintf( "failed to find supported API. Available %s API: %v. Required major version: %d. Minimum required minor version: %d", e.ApiKind, e.SupportedVersions, RequiredMajorVersion, MinimumRequiredMinorVersion, ) } func ToCloudVersion(version string) (APIVersion, error) { versionParts := strings.Split(version, ".") if len(versionParts) != 2 { return APIVersion{}, errors.New("invalid version format") } major, err := strconv.Atoi(versionParts[0]) if err != nil { return APIVersion{}, errors.New("invalid major version") } minor, err := strconv.Atoi(versionParts[1]) if err != nil { return APIVersion{}, errors.New("invalid minor version") } return APIVersion{ Major: major, Minor: minor, }, nil } type ApiEndpointDescription struct { Versions []ApiVersionDescription `json:"versions"` } type ApiVersionDescription struct { Version string `json:"version"` URL string `json:"url"` } type ApiDescriptions struct { API ApiEndpointDescription `json:"api"` Linters ApiEndpointDescription `json:"linters"` } func selectSupportedVersion(apiDescriptions []ApiVersionDescription) string { for _, version := range apiDescriptions { cloudVersion, err := ToCloudVersion(version.Version) if err != nil { log.Fatalf("Failed to parse cloud version: %v", err) } if cloudVersion.Major == RequiredMajorVersion && cloudVersion.Minor >= MinimumRequiredMinorVersion { return version.URL } } return "" } func (endpoint *QdRootEndpoint) requestApiEndpoints() (*QdApiEndpoints, error) { httpClient := &http.Client{ Timeout: getRequestTimeout(), } return endpoint.requestApiEndpointsCustomClient(httpClient) } func (endpoint *QdRootEndpoint) requestApiEndpointsCustomClient(httpClient *http.Client) (*QdApiEndpoints, error) { client := QdClient{ httpClient: httpClient, apiUrl: endpoint.Url, } request := NewCloudRequest(VersionsURI) response, err := client.doRequest(&request) if err != nil { return nil, fmt.Errorf("request of available API versions failed: %w", err) } var apiDescriptions ApiDescriptions err = json.Unmarshal(response, &apiDescriptions) if err != nil { return nil, fmt.Errorf("failed to unmarshal API descriptions: %w", err) } apiEndpoints := QdApiEndpoints{RootEndpoint: endpoint} apiEndpoints.CloudApiUrl = selectSupportedVersion(apiDescriptions.API.Versions) if apiEndpoints.CloudApiUrl == "" { return nil, &ApiVersionMismatchError{"cloud", extractVersions(apiDescriptions.API.Versions)} } apiEndpoints.LintersApiUrl = selectSupportedVersion(apiDescriptions.Linters.Versions) if apiEndpoints.LintersApiUrl == "" { return nil, &ApiVersionMismatchError{"linters", extractVersions(apiDescriptions.Linters.Versions)} } return &apiEndpoints, err } func extractVersions(descriptions []ApiVersionDescription) []string { var versions []string for _, apiVersion := range descriptions { versions = append(versions, apiVersion.Version) } return versions }