weldr: Update projects/source/info to support API v1
This commit changes the store.GetAllSources to distinguish between getting the source by the Name field, or by the ID (the key to the map) using GetAllSourcesByName and ...ByID. SourceConfig.RepoConfig() now takes an id parameter because SourceConfig only stores the Name, not the ID. In weldr I split the sourceInfoHandler into 2 separate functions for v0 and v1 behavior, with the core of the old function refactored as getSourceConfigs and used by both of them. This also adds new structs for the SourceResponseV0 and SourceResponseV1 as well as helper functions for converting to/from store.SourceConfig
This commit is contained in:
parent
ddd2010815
commit
982d292a96
5 changed files with 193 additions and 27 deletions
|
|
@ -45,7 +45,21 @@ func GetSourceInfoV0(socket *http.Client, sourceNames string) (map[string]weldr.
|
|||
if resp != nil || err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
var info weldr.SourceInfoV0
|
||||
var info weldr.SourceInfoResponseV0
|
||||
err = json.Unmarshal(body, &info)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return info.Sources, nil, nil
|
||||
}
|
||||
|
||||
// GetSourceInfoV1 returns detailed information on the named sources
|
||||
func GetSourceInfoV1(socket *http.Client, sourceNames string) (map[string]weldr.SourceConfigV1, *APIResponse, error) {
|
||||
body, resp, err := GetRaw(socket, "GET", "/api/v1/projects/source/info/"+sourceNames)
|
||||
if resp != nil || err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
var info weldr.SourceInfoResponseV1
|
||||
err = json.Unmarshal(body, &info)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ func TestListSourcesV1(t *testing.T) {
|
|||
require.Contains(t, list, "package-repo-2")
|
||||
}
|
||||
|
||||
// Get the source info
|
||||
// Get the source info using the v0 API
|
||||
func TestGetSourceInfoV0(t *testing.T) {
|
||||
source := `
|
||||
name = "package-repo-info-v0"
|
||||
|
|
@ -348,6 +348,37 @@ func TestGetSourceInfoV0(t *testing.T) {
|
|||
require.True(t, resp.Status, "DELETE source failed: %#v", resp)
|
||||
}
|
||||
|
||||
// Get the source info using the v1 API
|
||||
func TestGetSourceInfoV1(t *testing.T) {
|
||||
source := `
|
||||
id = "package-repo-info-v1"
|
||||
name = "repo for info test v1"
|
||||
url = "file://REPO-PATH"
|
||||
type = "yum-baseurl"
|
||||
proxy = "https://proxy-url/"
|
||||
check_ssl = true
|
||||
check_gpg = true
|
||||
gpgkey_urls = ["https://url/path/to/gpg-key"]
|
||||
`
|
||||
source = strings.Replace(source, "REPO-PATH", testState.repoDir, 1)
|
||||
|
||||
resp, err := PostTOMLSourceV1(testState.socket, source)
|
||||
require.NoError(t, err, "POST source failed with a client error")
|
||||
require.True(t, resp.Status, "POST source failed: %#v", resp)
|
||||
|
||||
info, resp, err := GetSourceInfoV1(testState.socket, "package-repo-info-v1")
|
||||
require.NoError(t, err, "GET source failed with a client error")
|
||||
require.Nil(t, resp, "GET source failed: %#v", resp)
|
||||
require.Contains(t, info, "package-repo-info-v1", "No source info returned")
|
||||
require.Equal(t, "repo for info test v1", info["package-repo-info-v1"].Name)
|
||||
require.Equal(t, "file://"+testState.repoDir, info["package-repo-info-v1"].URL)
|
||||
|
||||
// TODO update for DeleteJSONSourceV1
|
||||
resp, err = DeleteSourceV0(testState.socket, "package-repo-info-v1")
|
||||
require.NoError(t, err, "DELETE source failed with a client error")
|
||||
require.True(t, resp.Status, "DELETE source failed: %#v", resp)
|
||||
}
|
||||
|
||||
func UploadUserDefinedSourcesV0(t *testing.T, sources []string) {
|
||||
for i := range sources {
|
||||
source := strings.Replace(sources[i], "REPO-PATH", testState.repoDir, 1)
|
||||
|
|
|
|||
|
|
@ -464,7 +464,22 @@ func (s *Store) GetSource(name string) *SourceConfig {
|
|||
return &source
|
||||
}
|
||||
|
||||
func (s *Store) GetAllSources() map[string]SourceConfig {
|
||||
// GetAllSourcesByName returns the sources using the repo name as the key
|
||||
func (s *Store) GetAllSourcesByName() map[string]SourceConfig {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
sources := make(map[string]SourceConfig)
|
||||
|
||||
for _, v := range s.sources {
|
||||
sources[v.Name] = v
|
||||
}
|
||||
|
||||
return sources
|
||||
}
|
||||
|
||||
// GetAllSourcesByID returns the sources using the repo id as the key
|
||||
func (s *Store) GetAllSourcesByID() map[string]SourceConfig {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
|
|
@ -499,10 +514,10 @@ func NewSourceConfig(repo rpmmd.RepoConfig, system bool) SourceConfig {
|
|||
return sc
|
||||
}
|
||||
|
||||
func (s *SourceConfig) RepoConfig() rpmmd.RepoConfig {
|
||||
func (s *SourceConfig) RepoConfig(name string) rpmmd.RepoConfig {
|
||||
var repo rpmmd.RepoConfig
|
||||
|
||||
repo.Name = s.Name
|
||||
repo.Name = name
|
||||
repo.IgnoreSSL = !s.CheckSSL
|
||||
|
||||
if s.Type == "yum-baseurl" {
|
||||
|
|
|
|||
|
|
@ -380,18 +380,9 @@ func (api *API) sourceEmptyInfoHandler(writer http.ResponseWriter, request *http
|
|||
statusResponseError(writer, http.StatusNotFound, errors)
|
||||
}
|
||||
|
||||
func (api *API) sourceInfoHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
|
||||
if !verifyRequestVersion(writer, params, 0) { // TODO: version 1 API
|
||||
return
|
||||
}
|
||||
|
||||
// weldr uses a slightly different format than dnf to store repository
|
||||
// configuration
|
||||
type reply struct {
|
||||
Sources map[string]store.SourceConfig `json:"sources"`
|
||||
Errors []responseError `json:"errors"`
|
||||
}
|
||||
|
||||
// getSourceConfigs retrieves the list of sources from the system repos an store
|
||||
// Returning a list of store.SourceConfig entries indexed by the id of the source
|
||||
func (api *API) getSourceConfigs(params httprouter.Params) (map[string]store.SourceConfig, []responseError) {
|
||||
names := params.ByName("sources")
|
||||
|
||||
sources := map[string]store.SourceConfig{}
|
||||
|
|
@ -399,7 +390,7 @@ func (api *API) sourceInfoHandler(writer http.ResponseWriter, request *http.Requ
|
|||
|
||||
// if names is "*" we want all sources
|
||||
if names == "*" {
|
||||
sources = api.store.GetAllSources()
|
||||
sources = api.store.GetAllSourcesByID()
|
||||
for _, repo := range api.repos {
|
||||
sources[repo.Name] = store.NewSourceConfig(repo, true)
|
||||
}
|
||||
|
|
@ -419,7 +410,7 @@ func (api *API) sourceInfoHandler(writer http.ResponseWriter, request *http.Requ
|
|||
}
|
||||
// check if the source is in the store
|
||||
if source := api.store.GetSource(name); source != nil {
|
||||
sources[source.Name] = *source
|
||||
sources[name] = *source
|
||||
} else {
|
||||
error := responseError{
|
||||
ID: "UnknownSource",
|
||||
|
|
@ -430,6 +421,37 @@ func (api *API) sourceInfoHandler(writer http.ResponseWriter, request *http.Requ
|
|||
}
|
||||
}
|
||||
|
||||
return sources, errors
|
||||
}
|
||||
|
||||
// sourceInfoHandler routes the call to the correct API version handler
|
||||
func (api *API) sourceInfoHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
|
||||
versionString := params.ByName("version")
|
||||
version, err := strconv.ParseUint(versionString, 10, 0)
|
||||
if err != nil {
|
||||
notFoundHandler(writer, nil)
|
||||
return
|
||||
}
|
||||
switch version {
|
||||
case 0:
|
||||
api.sourceInfoHandlerV0(writer, request, params)
|
||||
case 1:
|
||||
api.sourceInfoHandlerV1(writer, request, params)
|
||||
default:
|
||||
notFoundHandler(writer, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// sourceInfoHandlerV0 handles the API v0 response
|
||||
func (api *API) sourceInfoHandlerV0(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
|
||||
sources, errors := api.getSourceConfigs(params)
|
||||
|
||||
// V0 responses use the source name as the key
|
||||
v0Sources := make(map[string]SourceConfigV0, len(sources))
|
||||
for _, s := range sources {
|
||||
v0Sources[s.Name] = NewSourceConfigV0(s)
|
||||
}
|
||||
|
||||
q, err := url.ParseQuery(request.URL.RawQuery)
|
||||
if err != nil {
|
||||
errors := responseError{
|
||||
|
|
@ -442,8 +464,8 @@ func (api *API) sourceInfoHandler(writer http.ResponseWriter, request *http.Requ
|
|||
|
||||
format := q.Get("format")
|
||||
if format == "json" || format == "" {
|
||||
err := json.NewEncoder(writer).Encode(reply{
|
||||
Sources: sources,
|
||||
err := json.NewEncoder(writer).Encode(SourceInfoResponseV0{
|
||||
Sources: v0Sources,
|
||||
Errors: errors,
|
||||
})
|
||||
common.PanicOnError(err)
|
||||
|
|
@ -458,8 +480,48 @@ func (api *API) sourceInfoHandler(writer http.ResponseWriter, request *http.Requ
|
|||
Msg: fmt.Sprintf("invalid format parameter: %s", format),
|
||||
}
|
||||
statusResponseError(writer, http.StatusBadRequest, errors)
|
||||
}
|
||||
}
|
||||
|
||||
// sourceInfoHandlerV1 handles the API v0 response
|
||||
func (api *API) sourceInfoHandlerV1(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
|
||||
sources, errors := api.getSourceConfigs(params)
|
||||
|
||||
// V1 responses use the source id as the key
|
||||
v1Sources := make(map[string]SourceConfigV1, len(sources))
|
||||
for id, s := range sources {
|
||||
v1Sources[id] = NewSourceConfigV1(id, s)
|
||||
}
|
||||
|
||||
q, err := url.ParseQuery(request.URL.RawQuery)
|
||||
if err != nil {
|
||||
errors := responseError{
|
||||
ID: "InvalidChars",
|
||||
Msg: fmt.Sprintf("invalid query string: %v", err),
|
||||
}
|
||||
statusResponseError(writer, http.StatusBadRequest, errors)
|
||||
return
|
||||
}
|
||||
|
||||
format := q.Get("format")
|
||||
if format == "json" || format == "" {
|
||||
err := json.NewEncoder(writer).Encode(SourceInfoResponseV1{
|
||||
Sources: v1Sources,
|
||||
Errors: errors,
|
||||
})
|
||||
common.PanicOnError(err)
|
||||
} else if format == "toml" {
|
||||
encoder := toml.NewEncoder(writer)
|
||||
encoder.Indent = ""
|
||||
err := encoder.Encode(sources)
|
||||
common.PanicOnError(err)
|
||||
} else {
|
||||
errors := responseError{
|
||||
ID: "InvalidChars",
|
||||
Msg: fmt.Sprintf("invalid format parameter: %s", format),
|
||||
}
|
||||
statusResponseError(writer, http.StatusBadRequest, errors)
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeSourceConfigV0 parses a request.Body into a SourceConfigV0
|
||||
|
|
@ -2391,8 +2453,8 @@ func getPkgNameGlob(pkg blueprint.Package) string {
|
|||
// Returns all configured repositories (base + sources) as rpmmd.RepoConfig
|
||||
func (api *API) allRepositories() []rpmmd.RepoConfig {
|
||||
repos := append([]rpmmd.RepoConfig{}, api.repos...)
|
||||
for _, source := range api.store.GetAllSources() {
|
||||
repos = append(repos, source.RepoConfig())
|
||||
for id, source := range api.store.GetAllSourcesByID() {
|
||||
repos = append(repos, source.RepoConfig(id))
|
||||
}
|
||||
return repos
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ func (s *SourceInfoV0) SourceConfig(sourceName string) (ssc store.SourceConfig,
|
|||
return si.SourceConfig(), true
|
||||
}
|
||||
|
||||
// SourceConfig interface defines the common functions needed to query the SourceConfigV0/V1 structs
|
||||
type SourceConfig interface {
|
||||
GetKey() string
|
||||
GetName() string
|
||||
|
|
@ -113,6 +114,21 @@ type SourceConfig interface {
|
|||
SourceConfig() store.SourceConfig
|
||||
}
|
||||
|
||||
// NewSourceConfigV0 converts a store.SourceConfig to a SourceConfigV0
|
||||
// The store does not support proxy and gpgkey_urls
|
||||
func NewSourceConfigV0(s store.SourceConfig) SourceConfigV0 {
|
||||
var sc SourceConfigV0
|
||||
|
||||
sc.Name = s.Name
|
||||
sc.Type = s.Type
|
||||
sc.URL = s.URL
|
||||
sc.CheckGPG = s.CheckGPG
|
||||
sc.CheckSSL = s.CheckSSL
|
||||
sc.System = s.System
|
||||
|
||||
return sc
|
||||
}
|
||||
|
||||
// SourceConfigV0 holds the source repository information
|
||||
type SourceConfigV0 struct {
|
||||
Name string `json:"name" toml:"name"`
|
||||
|
|
@ -121,8 +137,8 @@ type SourceConfigV0 struct {
|
|||
CheckGPG bool `json:"check_gpg" toml:"check_gpg"`
|
||||
CheckSSL bool `json:"check_ssl" toml:"check_ssl"`
|
||||
System bool `json:"system" toml:"system"`
|
||||
Proxy string `json:"proxy" toml:"proxy"`
|
||||
GPGUrls []string `json:"gpgkey_urls" toml:"gpgkey_urls"`
|
||||
Proxy string `json:"proxy,omitempty" toml:"proxy,omitempty"`
|
||||
GPGUrls []string `json:"gpgkey_urls,omitempty" toml:"gpgkey_urls,omitempty"`
|
||||
}
|
||||
|
||||
// Key return the key, .Name in this case
|
||||
|
|
@ -152,6 +168,28 @@ func (s SourceConfigV0) SourceConfig() (ssc store.SourceConfig) {
|
|||
return ssc
|
||||
}
|
||||
|
||||
// SourceInfoResponseV0
|
||||
type SourceInfoResponseV0 struct {
|
||||
Sources map[string]SourceConfigV0 `json:"sources"`
|
||||
Errors []responseError `json:"errors"`
|
||||
}
|
||||
|
||||
// NewSourceConfigV1 converts a store.SourceConfig to a SourceConfigV1
|
||||
// The store does not support proxy and gpgkey_urls
|
||||
func NewSourceConfigV1(id string, s store.SourceConfig) SourceConfigV1 {
|
||||
var sc SourceConfigV1
|
||||
|
||||
sc.ID = id
|
||||
sc.Name = s.Name
|
||||
sc.Type = s.Type
|
||||
sc.URL = s.URL
|
||||
sc.CheckGPG = s.CheckGPG
|
||||
sc.CheckSSL = s.CheckSSL
|
||||
sc.System = s.System
|
||||
|
||||
return sc
|
||||
}
|
||||
|
||||
// SourceConfigV1 holds the source repository information
|
||||
type SourceConfigV1 struct {
|
||||
ID string `json:"id" toml:"id"`
|
||||
|
|
@ -161,8 +199,8 @@ type SourceConfigV1 struct {
|
|||
CheckGPG bool `json:"check_gpg" toml:"check_gpg"`
|
||||
CheckSSL bool `json:"check_ssl" toml:"check_ssl"`
|
||||
System bool `json:"system" toml:"system"`
|
||||
Proxy string `json:"proxy" toml:"proxy"`
|
||||
GPGUrls []string `json:"gpgkey_urls" toml:"gpgkey_urls"`
|
||||
Proxy string `json:"proxy,omitempty" toml:"proxy,omitempty"`
|
||||
GPGUrls []string `json:"gpgkey_urls,omitempty" toml:"gpgkey_urls,omitempty"`
|
||||
}
|
||||
|
||||
// Key returns the key, .ID in this case
|
||||
|
|
@ -192,6 +230,12 @@ func (s SourceConfigV1) SourceConfig() (ssc store.SourceConfig) {
|
|||
return ssc
|
||||
}
|
||||
|
||||
// SourceInfoResponseV1
|
||||
type SourceInfoResponseV1 struct {
|
||||
Sources map[string]SourceConfigV1 `json:"sources"`
|
||||
Errors []responseError `json:"errors"`
|
||||
}
|
||||
|
||||
// ProjectsListV0 is the response to /projects/list request
|
||||
type ProjectsListV0 struct {
|
||||
Total uint `json:"total"`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue