[gnome-software] snap: Refactor refine code



commit aa19129ca6d3105b1016596e78f3a6005fb52ea3
Author: Robert Ancell <robert ancell canonical com>
Date:   Tue Jun 13 16:50:50 2017 +1200

    snap: Refactor refine code
    
    The existing code didn't get store information for installed snaps and did
    refines on add_installed etc instead of waiting for the refine call. The
    new code always goes to the store which is not strictly necessary but
    requires more refine flag for us to avoid. We now cache store snap information
    to minimise round trips.

 plugins/snap/gs-plugin-snap.c |  382 +++++++++++++++++++++--------------------
 1 files changed, 198 insertions(+), 184 deletions(-)
---
diff --git a/plugins/snap/gs-plugin-snap.c b/plugins/snap/gs-plugin-snap.c
index 4d093b6..936f00d 100644
--- a/plugins/snap/gs-plugin-snap.c
+++ b/plugins/snap/gs-plugin-snap.c
@@ -26,6 +26,7 @@
 
 struct GsPluginData {
        GsAuth          *auth;
+       GHashTable      *store_snaps;
 };
 
 void
@@ -41,6 +42,9 @@ gs_plugin_initialize (GsPlugin *plugin)
                return;
        }
 
+       priv->store_snaps = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                  g_free, (GDestroyNotify) g_object_unref);
+
        priv->auth = gs_auth_new ("snapd");
        gs_auth_set_provider_name (priv->auth, "Snap Store");
        gs_auth_set_provider_schema (priv->auth, "com.ubuntu.UbuntuOne.GnomeSoftware");
@@ -143,172 +147,28 @@ gs_plugin_snap_set_app_pixbuf_from_data (GsApp *app, const gchar *buf, gsize cou
        return TRUE;
 }
 
-static gboolean
-refine_icon (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error)
-{
-       const gchar *icon_url;
-
-       icon_url = gs_app_get_metadata_item (app, "snap::icon");
-       if (icon_url == NULL || g_strcmp0 (icon_url, "") == 0) {
-               g_autoptr(AsIcon) icon = as_icon_new ();
-               as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
-               as_icon_set_name (icon, "package-x-generic");
-               gs_app_add_icon (app, icon);
-               return TRUE;
-       }
-
-       /* icon is optional, either loaded from snapd or from a URL */
-       if (g_str_has_prefix (icon_url, "/")) {
-               g_autoptr(SnapdClient) client = NULL;
-               g_autoptr(SnapdIcon) icon = NULL;
-
-               client = get_client (plugin, cancellable, error);
-               if (client == NULL)
-                       return FALSE;
-
-               icon = snapd_client_get_icon_sync (client, gs_app_get_id (app), cancellable, error);
-               if (icon == NULL)
-                       return FALSE;
-
-               if (!gs_plugin_snap_set_app_pixbuf_from_data (app,
-                                                             g_bytes_get_data (snapd_icon_get_data (icon), 
NULL),
-                                                             g_bytes_get_size (snapd_icon_get_data (icon)),
-                                                             error)) {
-                       g_prefix_error (error, "Failed to load %s: ", icon_url);
-                       return FALSE;
-               }
-       } else {
-               g_autoptr(SoupMessage) message = NULL;
-               g_autoptr(GdkPixbufLoader) loader = NULL;
-               message = soup_message_new (SOUP_METHOD_GET, icon_url);
-               if (message == NULL) {
-                       g_set_error (error,
-                                    GS_PLUGIN_ERROR,
-                                    GS_PLUGIN_ERROR_NOT_SUPPORTED,
-                                    "Failed to parse icon URL: %s",
-                                    icon_url);
-                       return FALSE;
-               }
-               soup_session_send_message (gs_plugin_get_soup_session (plugin), message);
-               if (!gs_plugin_snap_set_app_pixbuf_from_data (app,
-                                       (const gchar *) message->response_body->data,
-                                       message->response_body->length,
-                                       error)) {
-                       g_prefix_error (error, "Failed to load %s: ", icon_url);
-                       return FALSE;
-               }
-       }
-
-       return TRUE;
-}
-
-static gboolean
-gs_plugin_snap_refine_app (GsPlugin *plugin,
-                          GsApp *app,
-                          GsPluginRefineFlags flags,
-                          SnapdSnap *snap,
-                          gboolean from_search,
-                          GCancellable *cancellable,
-                          GError **error)
+static GPtrArray *
+find_snaps (GsPlugin *plugin, SnapdFindFlags flags, const gchar *query, GCancellable *cancellable, GError 
**error)
 {
-       const gchar *icon_url, *launch_name = NULL;
-       const gchar *origin;
-       g_autofree gchar *origin_hostname = NULL;
-       g_autoptr(GdkPixbuf) icon_pixbuf = NULL;
-       GPtrArray *screenshots;
-
-       gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
-       switch (snapd_snap_get_status (snap)) {
-       case SNAPD_SNAP_STATUS_INSTALLED:
-       case SNAPD_SNAP_STATUS_ACTIVE:
-               if (gs_app_get_state (app) == AS_APP_STATE_AVAILABLE)
-                       gs_app_set_state (app, AS_APP_STATE_UNKNOWN);
-               gs_app_set_state (app, AS_APP_STATE_INSTALLED);
-               break;
-       case SNAPD_SNAP_STATUS_AVAILABLE:
-       case SNAPD_SNAP_STATUS_PRICED:
-               gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
-               break;
-       default:
-               g_warning ("Ignoring snap with unknown state");
-               return TRUE;
-       }
-       gs_app_set_name (app, GS_APP_QUALITY_HIGHEST, snapd_snap_get_name (snap));
-       gs_app_set_summary (app, GS_APP_QUALITY_HIGHEST, snapd_snap_get_summary (snap));
-       gs_app_set_description (app, GS_APP_QUALITY_HIGHEST, snapd_snap_get_description (snap));
-       gs_app_set_version (app, snapd_snap_get_version (snap));
-       if (snapd_snap_get_installed_size (snap) > 0)
-               gs_app_set_size_installed (app, snapd_snap_get_installed_size (snap));
-       if (snapd_snap_get_download_size (snap) > 0)
-               gs_app_set_size_download (app, snapd_snap_get_download_size (snap));
-       if (snapd_snap_get_install_date (snap) != NULL)
-               gs_app_set_install_date (app, g_date_time_to_unix (snapd_snap_get_install_date (snap)));
-       if (gs_plugin_check_distro_id (plugin, "ubuntu"))
-               gs_app_add_quirk (app, AS_APP_QUIRK_PROVENANCE);
-       if (snapd_snap_get_confinement (snap) == SNAPD_CONFINEMENT_STRICT)
-               gs_app_add_kudo (app, GS_APP_KUDO_SANDBOXED);
-
-       icon_url = snapd_snap_get_icon (snap);
-       if (icon_url != NULL)
-               gs_app_set_metadata (app, "snap::icon", icon_url);
-
-       /* assume the origin is where the icon is coming from */
-       if (icon_url != NULL && g_strcmp0 (icon_url, "") != 0 && !g_str_has_prefix (icon_url, "/"))
-               origin_hostname = g_strdup (icon_url);
-
-        if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON) {
-               if (!refine_icon (plugin, app, cancellable, error))
-                       return FALSE;
-       }
+       GsPluginData *priv = gs_plugin_get_data (plugin);
+       g_autoptr(SnapdClient) client = NULL;
+       g_autoptr(GPtrArray) snaps = NULL;
+       guint i;
 
-       screenshots = snapd_snap_get_screenshots (snap);
-       if (screenshots != NULL && screenshots->len > 0) {
-               guint i;
-
-               for (i = 0; i < screenshots->len; i++) {
-                       SnapdScreenshot *screenshot = screenshots->pdata[i];
-                       g_autoptr(AsScreenshot) ss = NULL;
-                       g_autoptr(AsImage) image = NULL;
-
-                       ss = as_screenshot_new ();
-                       as_screenshot_set_kind (ss, AS_SCREENSHOT_KIND_NORMAL);
-                       image = as_image_new ();
-                       as_image_set_url (image, snapd_screenshot_get_url (screenshot));
-                       as_image_set_kind (image, AS_IMAGE_KIND_SOURCE);
-                       as_image_set_width (image, snapd_screenshot_get_width (screenshot));
-                       as_image_set_height (image, snapd_screenshot_get_height (screenshot));
-                       as_screenshot_add_image (ss, image);
-                       gs_app_add_screenshot (app, ss);
-
-                       /* fall back to the screenshot */
-                       if (origin_hostname == NULL)
-                               origin_hostname = g_strdup (snapd_screenshot_get_url (screenshot));
-               }
-       }
+       client = get_client (plugin, cancellable, error);
+       if (client == NULL)
+               return FALSE;
+       snaps = snapd_client_find_sync (client, flags, query, NULL, cancellable, NULL);
+       if (snaps == NULL)
+               return NULL;
 
-       /* set the application origin */
-       if (gs_app_get_origin_hostname (app) == NULL) {
-               /* from the snap store */
-               origin = snapd_snap_get_developer (snap);
-               if (g_strcmp0 (origin, "canonical") == 0)
-                       gs_app_set_origin_hostname (app, "myapps.developer.ubuntu.com");
-               else if (origin_hostname != NULL)
-                       gs_app_set_origin_hostname (app, origin_hostname);
+       /* cache results */
+       for (i = 0; i < snaps->len; i++) {
+               SnapdSnap *snap = snaps->pdata[i];
+               g_hash_table_insert (priv->store_snaps, g_strdup (snapd_snap_get_name (snap)), g_object_ref 
(snap));
        }
 
-       if (!from_search) {
-               GPtrArray *apps;
-
-               apps = snapd_snap_get_apps (snap);
-               if (apps->len > 0)
-                       launch_name = snapd_app_get_name (apps->pdata[0]);
-
-               if (launch_name != NULL)
-                       gs_app_set_metadata (app, "snap::launch-name", launch_name);
-               else
-                       gs_app_add_quirk (app, AS_APP_QUIRK_NOT_LAUNCHABLE);
-       }
-       return TRUE;
+       return g_steal_pointer (&snaps);
 }
 
 gboolean
@@ -319,7 +179,6 @@ gs_plugin_url_to_app (GsPlugin *plugin,
                      GError **error)
 {
        g_autofree gchar *scheme = NULL;
-       g_autoptr(SnapdClient) client = NULL;
        g_autofree gchar *path = NULL;
        g_autoptr(GPtrArray) snaps = NULL;
        SnapdSnap *snap;
@@ -332,21 +191,18 @@ gs_plugin_url_to_app (GsPlugin *plugin,
 
        /* create app */
        path = gs_utils_get_url_path (url);
-       client = get_client (plugin, cancellable, error);
-       if (client == NULL)
-               return FALSE;
-       snaps = snapd_client_find_sync (client, SNAPD_FIND_FLAGS_MATCH_NAME, path, NULL, cancellable, NULL);
+       snaps = find_snaps (plugin, SNAPD_FIND_FLAGS_MATCH_NAME, path, cancellable, NULL);
        if (snaps == NULL || snaps->len < 1)
                return TRUE;
 
        snap = snaps->pdata[0];
        app = gs_app_new (snapd_snap_get_name (snap));
+       gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
        gs_app_set_scope (app, AS_APP_SCOPE_SYSTEM);
        gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_SNAP);
        gs_app_set_management_plugin (app, "snap");
        gs_app_add_quirk (app, AS_APP_QUIRK_NOT_REVIEWABLE);
-       if (!gs_plugin_snap_refine_app (plugin, app, GS_PLUGIN_REFINE_FLAGS_DEFAULT, snap, TRUE, cancellable, 
error))
-               return FALSE;
+       gs_app_set_name (app, GS_APP_QUALITY_HIGHEST, snapd_snap_get_name (snap));
        gs_app_list_add (list, app);
 
        return TRUE;
@@ -357,6 +213,7 @@ gs_plugin_destroy (GsPlugin *plugin)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
        g_clear_object (&priv->auth);
+       g_hash_table_unref (priv->store_snaps);
 }
 
 gboolean
@@ -385,12 +242,12 @@ gs_plugin_add_installed (GsPlugin *plugin,
 
                /* create a unique ID for deduplication, TODO: branch? */
                app = gs_app_new (snapd_snap_get_name (snap));
+               gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
                gs_app_set_scope (app, AS_APP_SCOPE_SYSTEM);
                gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_SNAP);
                gs_app_set_management_plugin (app, "snap");
                gs_app_add_quirk (app, AS_APP_QUIRK_NOT_REVIEWABLE);
-               if (!gs_plugin_snap_refine_app (plugin, app, GS_PLUGIN_REFINE_FLAGS_DEFAULT, snap, TRUE, 
cancellable, error))
-                       return FALSE;
+               gs_app_set_name (app, GS_APP_QUALITY_HIGHEST, snapd_snap_get_name (snap));
                gs_app_list_add (list, app);
        }
 
@@ -404,16 +261,12 @@ gs_plugin_add_search (GsPlugin *plugin,
                      GCancellable *cancellable,
                      GError **error)
 {
-       g_autoptr(SnapdClient) client = NULL;
        g_autofree gchar *query = NULL;
        g_autoptr(GPtrArray) snaps = NULL;
        guint i;
 
-       client = get_client (plugin, cancellable, error);
-       if (client == NULL)
-               return FALSE;
        query = g_strjoinv (" ", values);
-       snaps = snapd_client_find_sync (client, SNAPD_FIND_FLAGS_NONE, query, NULL, cancellable, error);
+       snaps = find_snaps (plugin, SNAPD_FIND_FLAGS_NONE, query, cancellable, error);
        if (snaps == NULL)
                return FALSE;
 
@@ -423,18 +276,93 @@ gs_plugin_add_search (GsPlugin *plugin,
 
                /* create a unique ID for deduplication, TODO: branch? */
                app = gs_app_new (snapd_snap_get_name (snap));
+               gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
                gs_app_set_scope (app, AS_APP_SCOPE_SYSTEM);
                gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_SNAP);
                gs_app_set_management_plugin (app, "snap");
                gs_app_add_quirk (app, AS_APP_QUIRK_NOT_REVIEWABLE);
-               if (!gs_plugin_snap_refine_app (plugin, app, GS_PLUGIN_REFINE_FLAGS_DEFAULT, snap, TRUE, 
cancellable, error))
-                       return FALSE;
+               gs_app_set_name (app, GS_APP_QUALITY_HIGHEST, snapd_snap_get_name (snap));
                gs_app_list_add (list, app);
        }
 
        return TRUE;
 }
 
+static gboolean
+load_icon (GsPlugin *plugin, GsApp *app, const gchar *icon_url, GCancellable *cancellable, GError **error)
+{
+       if (icon_url == NULL || g_strcmp0 (icon_url, "") == 0) {
+               g_autoptr(AsIcon) icon = as_icon_new ();
+               as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
+               as_icon_set_name (icon, "package-x-generic");
+               gs_app_add_icon (app, icon);
+               return TRUE;
+       }
+
+       /* icon is optional, either loaded from snapd or from a URL */
+       if (g_str_has_prefix (icon_url, "/")) {
+               g_autoptr(SnapdClient) client = NULL;
+               g_autoptr(SnapdIcon) icon = NULL;
+
+               client = get_client (plugin, cancellable, error);
+               if (client == NULL)
+                       return FALSE;
+
+               icon = snapd_client_get_icon_sync (client, gs_app_get_id (app), cancellable, error);
+               if (icon == NULL)
+                       return FALSE;
+
+               if (!gs_plugin_snap_set_app_pixbuf_from_data (app,
+                                                             g_bytes_get_data (snapd_icon_get_data (icon), 
NULL),
+                                                             g_bytes_get_size (snapd_icon_get_data (icon)),
+                                                             error)) {
+                       g_prefix_error (error, "Failed to load %s: ", icon_url);
+                       return FALSE;
+               }
+       } else {
+               g_autoptr(SoupMessage) message = NULL;
+               g_autoptr(GdkPixbufLoader) loader = NULL;
+               message = soup_message_new (SOUP_METHOD_GET, icon_url);
+               if (message == NULL) {
+                       g_set_error (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_NOT_SUPPORTED,
+                                    "Failed to parse icon URL: %s",
+                                    icon_url);
+                       return FALSE;
+               }
+               soup_session_send_message (gs_plugin_get_soup_session (plugin), message);
+               if (!gs_plugin_snap_set_app_pixbuf_from_data (app,
+                                       (const gchar *) message->response_body->data,
+                                       message->response_body->length,
+                                       error)) {
+                       g_prefix_error (error, "Failed to load %s: ", icon_url);
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+static SnapdSnap *
+get_store_snap (GsPlugin *plugin, const gchar *name, GCancellable *cancellable, GError **error)
+{
+       GsPluginData *priv = gs_plugin_get_data (plugin);
+       SnapdSnap *snap = NULL;
+       g_autoptr(GPtrArray) snaps = NULL;
+
+       /* use cached version if available */
+       snap = g_hash_table_lookup (priv->store_snaps, name);
+       if (snap != NULL)
+               return g_object_ref (snap);
+
+       snaps = find_snaps (plugin, SNAPD_FIND_FLAGS_MATCH_NAME, name, cancellable, error);
+       if (snaps == NULL || snaps->len < 1)
+               return NULL;
+
+       return g_object_ref (g_ptr_array_index (snaps, 0));
+}
+
 gboolean
 gs_plugin_refine_app (GsPlugin *plugin,
                      GsApp *app,
@@ -443,8 +371,9 @@ gs_plugin_refine_app (GsPlugin *plugin,
                      GError **error)
 {
        g_autoptr(SnapdClient) client = NULL;
-       const gchar *id;
-       g_autoptr(SnapdSnap) snap = NULL;
+       const gchar *id, *icon_url = NULL;
+       g_autoptr(SnapdSnap) local_snap = NULL;
+       g_autoptr(SnapdSnap) store_snap = NULL;
 
        /* not us */
        if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
@@ -456,11 +385,96 @@ gs_plugin_refine_app (GsPlugin *plugin,
        id = gs_app_get_id (app);
        if (id == NULL)
                id = gs_app_get_source_default (app);
-       snap = snapd_client_list_one_sync (client, id, cancellable, error);
-       if (snap == NULL)
-               return FALSE;
-       if (!gs_plugin_snap_refine_app (plugin, app, flags, snap, FALSE, cancellable, error))
-               return FALSE;
+
+       /* get information from installed snaps */
+       local_snap = snapd_client_list_one_sync (client, id, cancellable, NULL);
+       if (local_snap != NULL) {
+               GPtrArray *apps;
+               const gchar *launch_name = NULL;
+
+               if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN)
+                       gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+               gs_app_set_name (app, GS_APP_QUALITY_NORMAL, snapd_snap_get_name (local_snap));
+               gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, snapd_snap_get_summary (local_snap));
+               gs_app_set_description (app, GS_APP_QUALITY_NORMAL, snapd_snap_get_description (local_snap));
+               gs_app_set_version (app, snapd_snap_get_version (local_snap));
+               gs_app_set_size_installed (app, snapd_snap_get_installed_size (local_snap));
+               gs_app_set_install_date (app, g_date_time_to_unix (snapd_snap_get_install_date (local_snap)));
+               gs_app_set_developer_name (app, snapd_snap_get_developer (local_snap));
+               icon_url = snapd_snap_get_icon (local_snap);
+               if (g_strcmp0 (icon_url, "") == 0)
+                       icon_url = NULL;
+
+               apps = snapd_snap_get_apps (local_snap);
+               if (apps->len > 0)
+                       launch_name = snapd_app_get_name (apps->pdata[0]);
+               if (launch_name != NULL)
+                       gs_app_set_metadata (app, "snap::launch-name", launch_name);
+               else
+                       gs_app_add_quirk (app, AS_APP_QUIRK_NOT_LAUNCHABLE);
+       }
+
+       /* get information from snap store */
+       store_snap = get_store_snap (plugin, id, cancellable, NULL);
+       if (store_snap != NULL) {
+               GPtrArray *screenshots;
+               const gchar *screenshot_url = NULL;
+
+               if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN)
+                       gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+
+               gs_app_set_name (app, GS_APP_QUALITY_NORMAL, snapd_snap_get_name (store_snap));
+               gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, snapd_snap_get_summary (store_snap));
+               gs_app_set_description (app, GS_APP_QUALITY_NORMAL, snapd_snap_get_description (store_snap));
+               gs_app_set_version (app, snapd_snap_get_version (store_snap));
+               gs_app_set_size_download (app, snapd_snap_get_download_size (store_snap));
+               gs_app_set_developer_name (app, snapd_snap_get_developer (store_snap));
+               if (icon_url == NULL) {
+                       icon_url = snapd_snap_get_icon (store_snap);
+                       if (g_strcmp0 (icon_url, "") == 0)
+                               icon_url = NULL;
+               }
+               gs_app_set_developer_name (app, snapd_snap_get_developer (store_snap));
+
+               screenshots = snapd_snap_get_screenshots (store_snap);
+               if (screenshots != NULL && screenshots->len > 0) {
+                       guint i;
+
+                       for (i = 0; i < screenshots->len; i++) {
+                               SnapdScreenshot *screenshot = screenshots->pdata[i];
+                               g_autoptr(AsScreenshot) ss = NULL;
+                               g_autoptr(AsImage) image = NULL;
+
+                               ss = as_screenshot_new ();
+                               as_screenshot_set_kind (ss, AS_SCREENSHOT_KIND_NORMAL);
+                               image = as_image_new ();
+                               as_image_set_url (image, snapd_screenshot_get_url (screenshot));
+                               as_image_set_kind (image, AS_IMAGE_KIND_SOURCE);
+                               as_image_set_width (image, snapd_screenshot_get_width (screenshot));
+                               as_image_set_height (image, snapd_screenshot_get_height (screenshot));
+                               as_screenshot_add_image (ss, image);
+                               gs_app_add_screenshot (app, ss);
+
+                               /* fall back to the screenshot */
+                               if (screenshot_url == NULL)
+                                       screenshot_url = snapd_screenshot_get_url (screenshot);
+                       }
+               }
+
+               /* use some heuristics to guess the application origin */
+               if (gs_app_get_origin_hostname (app) == NULL) {
+                       if (icon_url != NULL && !g_str_has_prefix (icon_url, "/"))
+                               gs_app_set_origin_hostname (app, icon_url);
+                       else if (screenshot_url != NULL)
+                               gs_app_set_origin_hostname (app, screenshot_url);
+               }
+       }
+
+       /* load icon if requested */
+       if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON) {
+               if (!load_icon (plugin, app, icon_url, cancellable, error))
+                       return FALSE;
+       }
 
        return TRUE;
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]