From 2c28d1a0ca8452028c4e356ff352ff00cf01ace8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Wed, 24 Aug 2022 10:59:26 +0200 Subject: [PATCH] List an exact match first in package search (#1355) --- lib/livebook/runtime/dependencies.ex | 19 +++++++- test/livebook/runtime/dependencies_test.exs | 52 +++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/lib/livebook/runtime/dependencies.ex b/lib/livebook/runtime/dependencies.ex index 5bb8f4efc..cefc2831a 100644 --- a/lib/livebook/runtime/dependencies.ex +++ b/lib/livebook/runtime/dependencies.ex @@ -203,13 +203,17 @@ defmodule Livebook.Runtime.Dependencies do def search_hex(search, opts) do api_url = opts[:api_url] || "https://hex.pm/api" - params = %{"search" => "name:#{search}*", "sort" => "downloads"} + params = %{"search" => "name:#{search}*", "sort" => "recent_downloads"} url = api_url <> "/packages?" <> URI.encode_query(params) case Livebook.Utils.HTTP.request(:get, url) do {:ok, status, _headers, body} -> with 200 <- status, {:ok, packages} <- Jason.decode(body) do - packages = Enum.map(packages, &parse_package/1) + packages = + packages + |> Enum.map(&parse_package/1) + |> reorder_packages(search) + {:ok, packages} else _ -> {:error, "unexpected response"} @@ -231,4 +235,15 @@ defmodule Livebook.Runtime.Dependencies do dependency: dependency } end + + defp reorder_packages(packages, search) do + case Enum.find_index(packages, &(&1.name == search)) do + nil -> + packages + + exact_idx -> + {package, packages} = List.pop_at(packages, exact_idx) + [package | packages] + end + end end diff --git a/test/livebook/runtime/dependencies_test.exs b/test/livebook/runtime/dependencies_test.exs index 666fd9b48..a82dee5c5 100644 --- a/test/livebook/runtime/dependencies_test.exs +++ b/test/livebook/runtime/dependencies_test.exs @@ -265,6 +265,58 @@ defmodule Livebook.Runtime.DependenciesTest do ]} end + test "lists a package matching exactly first", %{bypass: bypass} do + Bypass.expect_once(bypass, "GET", "/api/packages", fn conn -> + conn + |> Plug.Conn.put_resp_content_type("application/json") + |> Plug.Conn.resp(200, ~S""" + [ + { + "configs": { + "erlang.mk": "dep_ecto_sql = hex 3.7.2", + "mix.exs": "{:ecto_sql, \"~> 3.7\"}", + "rebar.config": "{ecto_sql, \"3.7.2\"}" + }, + "docs_html_url": "https://hexdocs.pm/ecto_sql/", + "html_url": "https://hex.pm/packages/ecto_sql", + "latest_stable_version": "3.7.2", + "latest_version": "3.7.2", + "meta": { + "description": "SQL-based adapters for Ecto and database migrations", + "licenses": ["Apache-2.0"], + "links": { "GitHub": "https://github.com/elixir-ecto/ecto_sql" } + }, + "name": "ecto_sql", + "url": "https://hex.pm/api/packages/ecto_sql" + }, + { + "configs": { + "erlang.mk": "dep_ecto = hex 3.7.2", + "mix.exs": "{:ecto, \"~> 3.7\"}", + "rebar.config": "{ecto, \"3.7.2\"}" + }, + "docs_html_url": "https://hexdocs.pm/ecto/", + "html_url": "https://hex.pm/packages/ecto", + "latest_stable_version": "3.7.2", + "latest_version": "3.7.2", + "meta": { + "description": "A toolkit for data mapping and language integrated query for Elixir", + "licenses": ["Apache-2.0"], + "links": { "GitHub": "https://github.com/elixir-ecto/ecto" } + }, + "name": "ecto", + "url": "https://hex.pm/api/packages/ecto" + } + ] + """) + end) + + api_url = api_url(bypass.port) + + assert {:ok, [%{name: "ecto"}, %{name: "ecto_sql"}]} = + Dependencies.search_hex("ecto", api_url: api_url) + end + test "returns an error on unsuccessful API response", %{bypass: bypass} do Bypass.expect_once(bypass, "GET", "/api/packages", fn conn -> Plug.Conn.resp(conn, 500, "Error")