From patchwork Tue Feb 12 23:38:46 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Morris X-Patchwork-Id: 1000 Return-Path: Delivered-To: patchwork@archlinux.org Received: from apollo.archlinux.org (localhost [127.0.0.1]) by apollo.archlinux.org (Postfix) with ESMTP id 14E21B798476 for ; Tue, 12 Feb 2019 23:40:08 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on apollo X-Spam-Level: X-Spam-Status: No, score=-1.2 required=5.0 tests=BAYES_00=-1, DKIM_ADSP_CUSTOM_MED=0.001,DKIM_INVALID=1,DKIM_SIGNED=0.1, FREEMAIL_FROM=0.5,LOCAL_CHARITY=1.5,MAILING_LIST_MULTI=-1, RCVD_IN_DNSWL_MED=-2.3,T_DMARC_POLICY_NONE=0.01, T_DMARC_SIMPLE_DKIM=0.01 autolearn=ham autolearn_force=no version=3.4.2 X-Spam-BL-Results: [127.0.0.10] [127.0.9.2] [127.0.0.10] Received: from orion.archlinux.org (orion.archlinux.org [88.198.91.70]) by apollo.archlinux.org (Postfix) with ESMTPS for ; Tue, 12 Feb 2019 23:40:08 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id 63D96109978B08; Tue, 12 Feb 2019 23:40:06 +0000 (UTC) Received: from luna.archlinux.org (luna.archlinux.org [IPv6:2a01:4f8:160:3033::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by orion.archlinux.org (Postfix) with ESMTPS; Tue, 12 Feb 2019 23:40:06 +0000 (UTC) Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id 269952BF80; Tue, 12 Feb 2019 23:40:06 +0000 (UTC) Authentication-Results: luna.archlinux.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=dN0UV8Yl Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id 648372BF7D for ; Tue, 12 Feb 2019 23:40:01 +0000 (UTC) Received: from orion.archlinux.org (orion.archlinux.org [IPv6:2a01:4f8:160:6087::1]) by luna.archlinux.org (Postfix) with ESMTPS for ; Tue, 12 Feb 2019 23:40:01 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id 0F878109978B05 for ; Tue, 12 Feb 2019 23:40:00 +0000 (UTC) Received: from mail-pl1-x641.google.com (mail-pl1-x641.google.com [IPv6:2607:f8b0:4864:20::641]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by orion.archlinux.org (Postfix) with ESMTPS for ; Tue, 12 Feb 2019 23:39:59 +0000 (UTC) Received: by mail-pl1-x641.google.com with SMTP id o6so206329pls.13 for ; Tue, 12 Feb 2019 15:39:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=ndaWy3H+6FExcHx6sKhReIxmJvO0aOIuwr6BGVWAlfk=; b=dN0UV8YldwEptbgshXkwmduV2+laSMxqQYbX8gPiVZHwIL93pKgxVLZvLKSjS2viei ilD8qcZPvu4QwDvERMNZ89fS2mPIrmFoeSnUWAg9LLAQL7HJ3Aj9rcqLlux7CevYJRQa HouhOAJ01HrzCphv2b30rfNer/FH9jXfrVHnbcCQXFI/bTQx1J9KST401eO6zawiFAj8 0EYxnwIKZRrCXC+hV9MrFNNFhpBnZRyygBcMwlMsZ1buMMF2zDCUniJdFb9kf9oO+xM3 j2qcUMKWKRB9luBCUaYR0wtrp9o9fs+fwTV9m3ySPflyIUby6G+65ZSYn63eUHLGlmn1 OhJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=ndaWy3H+6FExcHx6sKhReIxmJvO0aOIuwr6BGVWAlfk=; b=M47yoKfYiqw7Gi89XKrL641fuq3BNWFdiqyCXg/XKcB6MaBMwfkeUcauepmFbS+cc0 /fUNnqdq6NkNYEScyqvU+zR/iMaxJ6GPwV/CMQhg6zaRs1DIRF/lyYHVJCMGeUUdSRKO nscYpFIbtBbid7KP7AfzNDPnOgq1r4C9WKZ0heLEFcVyFU8geoaLuwPuN4iFqqf1JDTG G94A7MVbhcgWlnS79mvr8vp0BQY8kGj4O7smIkhEr9zOJZiPgCX0BGc4sVOfwkbx4eua /7IakU9rbiBkqmTnNfucol35bDWyiCKpD1P8UhQRJSoNcukaDAVtaQyu9BcwZlVFy/Ng 0HBg== X-Gm-Message-State: AHQUAuatdxiezuusLvAxpC3ZYnGCNJ7gpJXn0LtHlT5izD/yl6JfM3S6 /ybPtETXcw0emPYapkvsPczgAUmHpWw= X-Google-Smtp-Source: AHgI3IZNAD+5V3WPmlgZse/RGLvhcUzOVfIFKv8doGrzuC7H5uGQYPfHvd0QFhSU5SfZFr6OpDoWJA== X-Received: by 2002:a17:902:27e6:: with SMTP id i35mr6525825plg.222.1550014797718; Tue, 12 Feb 2019 15:39:57 -0800 (PST) Received: from localhost.localdomain ([73.93.142.160]) by smtp.gmail.com with ESMTPSA id v190sm20805324pfv.26.2019.02.12.15.39.56 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 12 Feb 2019 15:39:56 -0800 (PST) From: Kevin Morris To: aur-dev@archlinux.org Subject: [PATCH] Introduce API v6 Date: Tue, 12 Feb 2019 15:38:46 -0800 Message-Id: <20190212233846.16002-1-kevr.gtalk@gmail.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 X-BeenThere: aur-dev@archlinux.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Arch User Repository \(AUR\) Development" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: aur-dev-bounces@archlinux.org Sender: "aur-dev" Add new method of multiple by[] search in API v6. The 'provides' by field is also supported in the new multiple by[] method included with this patch. 'provides' may not be used in legacy (v5) singular by search. A helpful json error message is returned to the user to indicate so. This patch is a follow-up implementation to https://patchwork.archlinux.org/patch/479/ Signed-off-by: Kevin Morris --- doc/rpc.txt | 24 ++++++--- web/lib/aurjson.class.php | 105 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 13 deletions(-) diff --git a/doc/rpc.txt b/doc/rpc.txt index 3148ebe..486a49d 100644 --- a/doc/rpc.txt +++ b/doc/rpc.txt @@ -5,7 +5,7 @@ Package Search -------------- Package searches can be performed by issuing HTTP GET requests of the form -+/rpc/?v=5&type=search&by=_field_&arg=_keywords_+ where _keywords_ is the ++/rpc/?v=6&type=search&by[]=_field_&arg[]=_keywords_+ where _keywords_ is the search argument and _field_ is one of the following values: * `name` (search by package name only) @@ -15,31 +15,39 @@ search argument and _field_ is one of the following values: * `makedepends` (search for packages that makedepend on _keywords_) * `optdepends` (search for packages that optdepend on _keywords_) * `checkdepends` (search for packages that checkdepend on _keywords_) +* `provides` (search by package provides; v6 multiple by[] only) The _by_ parameter can be skipped and defaults to `name-desc`. If a maintainer search is performed and the search argument is left empty, a list of orphan packages is returned. +Note: Legacy v5 support is still enabled for singular _by_ searches. + Package Details --------------- Package information can be obtained by issuing HTTP GET requests of the form -+/rpc/?v=5&type=info&arg[]=_pkg1_&arg[]=_pkg2_&...+ where _pkg1_, _pkg2_, ... ++/rpc/?v=6&type=info&arg[]=_pkg1_&arg[]=_pkg2_&...+ where _pkg1_, _pkg2_, ... are the names of packages to retrieve package details for. Examples -------- `search`:: - `/rpc/?v=5&type=search&arg=foobar` + `/rpc/?v=6&type=search&arg=foobar` `search` by maintainer:: - `/rpc/?v=5&type=search&by=maintainer&arg=john` + `/rpc/?v=6&type=search&by[]=maintainer&arg=john` `search` packages that have _boost_ as `makedepends`:: - `/rpc/?v=5&type=search&by=makedepends&arg=boost` + `/rpc/?v=6&type=search&by[]=makedepends&arg=boost` `search` with callback:: - `/rpc/?v=5&type=search&arg=foobar&callback=jsonp1192244621103` + `/rpc/?v=6&type=search&arg=foobar&callback=jsonp1192244621103` +`search` by provides:: + `/rpc/?v=6&type=search&by[]=provides&arg[]=gcc` +`search` by provides and name:: + `/rpc/?v=6&type=search&by[]=name&by[]=provides&arg[]=gcc` `info`:: - `/rpc/?v=5&type=info&arg[]=foobar` + `/rpc/?v=6&type=info&arg[]=foobar` `info` with multiple packages:: - `/rpc/?v=5&type=info&arg[]=foo&arg[]=bar` + `/rpc/?v=6&type=info&arg[]=foo&arg[]=bar` + diff --git a/web/lib/aurjson.class.php b/web/lib/aurjson.class.php index c275d21..ab4eb19 100644 --- a/web/lib/aurjson.class.php +++ b/web/lib/aurjson.class.php @@ -80,7 +80,8 @@ class AurJSON { if (isset($http_data['v'])) { $this->version = intval($http_data['v']); } - if ($this->version < 1 || $this->version > 5) { + + if ($this->version < 1 || $this->version > 6) { return $this->json_error('Invalid version specified.'); } @@ -94,8 +95,14 @@ class AurJSON { if (isset($http_data['search_by']) && !isset($http_data['by'])) { $http_data['by'] = $http_data['search_by']; } - if (isset($http_data['by']) && !in_array($http_data['by'], self::$exposed_fields)) { - return $this->json_error('Incorrect by field specified.'); + + if (isset($http_data['by']) && !is_array($http_data['by'])) { + if ($http_data['by'] === 'provides') { + return $this->json_error("The 'provides' by field " . + "may only be used via multiple by[] search."); + } elseif (!in_array($http_data['by'], self::$exposed_fields)) { + return $this->json_error('Incorrect by field specified.'); + } } $this->dbh = DB::connect(); @@ -360,7 +367,7 @@ class AurJSON { } elseif ($this->version >= 2) { if ($this->version == 2 || $this->version == 3) { $fields = implode(',', self::$fields_v2); - } else if ($this->version == 4 || $this->version == 5) { + } else if ($this->version == 4 || $this->version == 5 || $this->version == 6) { $fields = implode(',', self::$fields_v4); } $query = "SELECT {$fields} " . @@ -470,6 +477,10 @@ class AurJSON { * @return mixed Returns an array of package matches. */ private function search($http_data) { + if ($this->version == 6 && is_array($http_data['by'])) { + return $this->search_v6($http_data); + } + $keyword_string = $http_data['arg']; if (isset($http_data['by'])) { @@ -497,6 +508,8 @@ class AurJSON { $keyword_string = $this->dbh->quote($keyword_string); $where_condition = "Users.Username = $keyword_string "; } + } else if ($search_by === 'provides') { + return $this->json_error("The 'provides' by field is only available via multiple by[] search."); } else if (in_array($search_by, self::$exposed_depfields)) { if (empty($keyword_string)) { return $this->json_error('Query arg is empty.'); @@ -515,6 +528,89 @@ class AurJSON { return $this->process_query('search', $where_condition); } + /* + * Returns multiple-by, multiple-arg and/or search information + * a feature not included in the v5 and before RPC API, + * which introduces the beginnings of the v6 API. + * + * @param array $http_data Query parameters. + * + * @return mixed Returns an array of results containing the package data + */ + private function search_v6($http_data) { + if (!is_array($http_data['arg'])) { + $http_data['arg'] = array($http_data['arg']); + } + + $max_results = config_get_int('options', 'max_rpc_results'); + $fields = implode(',', self::$fields_v4); + + $query = "SELECT {$fields} FROM " . + "Packages LEFT JOIN PackageBases " . + "ON PackageBases.ID = Packages.PackageBaseID " . + "LEFT JOIN Users " . + "ON PackageBases.MaintainerUID = Users.ID " . + "LEFT JOIN PackageRelations " . + "ON PackageRelations.PackageID = Packages.ID " . + "AND PackageRelations.RelTypeID = 2 "; + + $where = array(); + + foreach($http_data['by'] as $by) { + if($by == 'provides') { + foreach ($http_data['arg'] as $provide) { + array_push($where, + "PackageRelations.RelName = " . + $this->dbh->quote($provide)); + } + } elseif($by == 'name') { + foreach ($http_data['arg'] as $name) { + array_push($where, "Packages.NAME LIKE " . + $this->dbh->quote('%' . $name . '%_')); + array_push($where, "Packages.Name = " . + $this->dbh->quote($name)); + } + } elseif($by == 'name-desc') { + foreach ($http_data['arg'] as $name) { + array_push($where, "Packages.NAME LIKE " . + $this->dbh->quote('%' . $name . '%_')); + array_push($where, "Packages.Name = " . + $this->dbh->quote($name)); + array_push($where, "Packages.Description LIKE " . + $this->dbh->quote('%' . $name . '%_')); + } + } elseif (in_array($by, self::$exposed_depfields)) { + foreach ($http_data['arg'] as $dep) { + $subquery = "SELECT PackageDepends.DepName FROM PackageDepends "; + $subquery .= "LEFT JOIN DependencyTypes "; + $subquery .= "ON PackageDepends.DepTypeID = DependencyTypes.ID "; + $subquery .= "WHERE PackageDepends.PackageID = Packages.ID "; + $subquery .= "AND DependencyTypes.Name = " . $this->dbh->quote($by); + $sub_condition = $this->dbh->quote($dep) . " IN (${subquery})"; + array_push($where, $sub_condition); + } + } else { + return $this->json_error( + "${by} is not supported in v6 multi-argument by search." + ); + } + } + + $query .= "WHERE " . implode(" OR ", $where) . " "; + "AND PackageBases.PackagerUID IS NOT NULL " . + "LIMIT ${max_results}"; + + $packages = array(); // Final list of packages + $result = $this->dbh->query($query); + + if ($result) { + while ($row = $result->fetch(PDO::FETCH_ASSOC)) + array_push($packages, $row); + } + + return $this->json_results('search', count($packages), $packages, NULL); + } + /* * Returns the info on a specific package. * @@ -569,7 +665,6 @@ class AurJSON { return $this->process_query('multiinfo', $where_condition); } - /* * Returns all the packages for a specific maintainer. *