code.gitea.io/gitea@v1.21.7/routers/api/packages/conan/search.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package conan
     5  
     6  import (
     7  	"net/http"
     8  	"strings"
     9  
    10  	conan_model "code.gitea.io/gitea/models/packages/conan"
    11  	user_model "code.gitea.io/gitea/models/user"
    12  	"code.gitea.io/gitea/modules/context"
    13  	"code.gitea.io/gitea/modules/json"
    14  	conan_module "code.gitea.io/gitea/modules/packages/conan"
    15  )
    16  
    17  // SearchResult contains the found recipe names
    18  type SearchResult struct {
    19  	Results []string `json:"results"`
    20  }
    21  
    22  // SearchRecipes searches all recipes matching the query
    23  func SearchRecipes(ctx *context.Context) {
    24  	q := ctx.FormTrim("q")
    25  
    26  	opts := parseQuery(ctx.Package.Owner, q)
    27  
    28  	results, err := conan_model.SearchRecipes(ctx, opts)
    29  	if err != nil {
    30  		apiError(ctx, http.StatusInternalServerError, err)
    31  		return
    32  	}
    33  
    34  	jsonResponse(ctx, http.StatusOK, &SearchResult{
    35  		Results: results,
    36  	})
    37  }
    38  
    39  // parseQuery creates search options for the given query
    40  func parseQuery(owner *user_model.User, query string) *conan_model.RecipeSearchOptions {
    41  	opts := &conan_model.RecipeSearchOptions{
    42  		OwnerID: owner.ID,
    43  	}
    44  
    45  	if query != "" {
    46  		parts := strings.Split(strings.ReplaceAll(query, "@", "/"), "/")
    47  
    48  		opts.Name = parts[0]
    49  		if len(parts) > 1 && parts[1] != "*" {
    50  			opts.Version = parts[1]
    51  		}
    52  		if len(parts) > 2 && parts[2] != "*" {
    53  			opts.User = parts[2]
    54  		}
    55  		if len(parts) > 3 && parts[3] != "*" {
    56  			opts.Channel = parts[3]
    57  		}
    58  	}
    59  
    60  	return opts
    61  }
    62  
    63  // SearchPackagesV1 searches all packages of a recipe (Conan v1 endpoint)
    64  func SearchPackagesV1(ctx *context.Context) {
    65  	searchPackages(ctx, true)
    66  }
    67  
    68  // SearchPackagesV2 searches all packages of a recipe (Conan v2 endpoint)
    69  func SearchPackagesV2(ctx *context.Context) {
    70  	searchPackages(ctx, false)
    71  }
    72  
    73  func searchPackages(ctx *context.Context, searchAllRevisions bool) {
    74  	rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference)
    75  
    76  	if !searchAllRevisions && rref.Revision == "" {
    77  		lastRevision, err := conan_model.GetLastRecipeRevision(ctx, ctx.Package.Owner.ID, rref)
    78  		if err != nil {
    79  			if err == conan_model.ErrRecipeReferenceNotExist {
    80  				apiError(ctx, http.StatusNotFound, err)
    81  			} else {
    82  				apiError(ctx, http.StatusInternalServerError, err)
    83  			}
    84  			return
    85  		}
    86  		rref = rref.WithRevision(lastRevision.Value)
    87  	} else {
    88  		has, err := conan_model.RecipeExists(ctx, ctx.Package.Owner.ID, rref)
    89  		if err != nil {
    90  			if err == conan_model.ErrRecipeReferenceNotExist {
    91  				apiError(ctx, http.StatusNotFound, err)
    92  			} else {
    93  				apiError(ctx, http.StatusInternalServerError, err)
    94  			}
    95  			return
    96  		}
    97  		if !has {
    98  			apiError(ctx, http.StatusNotFound, nil)
    99  			return
   100  		}
   101  	}
   102  
   103  	recipeRevisions := []*conan_model.PropertyValue{{Value: rref.Revision}}
   104  	if searchAllRevisions {
   105  		var err error
   106  		recipeRevisions, err = conan_model.GetRecipeRevisions(ctx, ctx.Package.Owner.ID, rref)
   107  		if err != nil {
   108  			apiError(ctx, http.StatusInternalServerError, err)
   109  			return
   110  		}
   111  	}
   112  
   113  	result := make(map[string]*conan_module.Conaninfo)
   114  
   115  	for _, recipeRevision := range recipeRevisions {
   116  		currentRef := rref
   117  		if recipeRevision.Value != "" {
   118  			currentRef = rref.WithRevision(recipeRevision.Value)
   119  		}
   120  		packageReferences, err := conan_model.GetPackageReferences(ctx, ctx.Package.Owner.ID, currentRef)
   121  		if err != nil {
   122  			if err == conan_model.ErrRecipeReferenceNotExist {
   123  				apiError(ctx, http.StatusNotFound, err)
   124  			} else {
   125  				apiError(ctx, http.StatusInternalServerError, err)
   126  			}
   127  			return
   128  		}
   129  		for _, packageReference := range packageReferences {
   130  			if _, ok := result[packageReference.Value]; ok {
   131  				continue
   132  			}
   133  			pref, _ := conan_module.NewPackageReference(currentRef, packageReference.Value, "")
   134  			lastPackageRevision, err := conan_model.GetLastPackageRevision(ctx, ctx.Package.Owner.ID, pref)
   135  			if err != nil {
   136  				if err == conan_model.ErrPackageReferenceNotExist {
   137  					apiError(ctx, http.StatusNotFound, err)
   138  				} else {
   139  					apiError(ctx, http.StatusInternalServerError, err)
   140  				}
   141  				return
   142  			}
   143  			pref = pref.WithRevision(lastPackageRevision.Value)
   144  			infoRaw, err := conan_model.GetPackageInfo(ctx, ctx.Package.Owner.ID, pref)
   145  			if err != nil {
   146  				if err == conan_model.ErrPackageReferenceNotExist {
   147  					apiError(ctx, http.StatusNotFound, err)
   148  				} else {
   149  					apiError(ctx, http.StatusInternalServerError, err)
   150  				}
   151  				return
   152  			}
   153  			var info *conan_module.Conaninfo
   154  			if err := json.Unmarshal([]byte(infoRaw), &info); err != nil {
   155  				apiError(ctx, http.StatusInternalServerError, err)
   156  				return
   157  			}
   158  			result[pref.Reference] = info
   159  		}
   160  	}
   161  
   162  	jsonResponse(ctx, http.StatusOK, result)
   163  }