From cf323691310a7305b7b2275ffbc61d9bae87043d Mon Sep 17 00:00:00 2001
From: Phil Hughes <me@iamphill.com>
Date: Thu, 28 Mar 2019 12:25:24 +0000
Subject: [PATCH] Changes GraphQL client to allow for multipart requests

This is to mainly allow for images to be uploaded in design management.
---
 app/assets/javascripts/lib/graphql.js         | 19 +++--
 doc/development/fe_guide/graphql.md           | 28 ++++--
 .../javascripts/design_management/graphql.js  | 43 ++++++----
 .../design_management/pages/design/index.vue  |  8 ++
 .../design_management/pages/index.vue         | 15 ++--
 .../queries/uploadDesign.graphql              |  4 +-
 .../javascripts/design_management/router.js   |  9 ++
 .../design_management/pages/index_spec.js     |  3 +-
 locale/gitlab.pot                             |  3 +
 package.json                                  |  3 +-
 yarn.lock                                     | 85 +++++++++++--------
 11 files changed, 137 insertions(+), 83 deletions(-)

diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js
index 64e4e899f4472..ae02559415c3b 100644
--- a/app/assets/javascripts/lib/graphql.js
+++ b/app/assets/javascripts/lib/graphql.js
@@ -1,11 +1,16 @@
-import ApolloClient from 'apollo-boost';
+import { ApolloClient } from 'apollo-client';
+import { InMemoryCache } from 'apollo-cache-inmemory';
+import { createUploadLink } from 'apollo-upload-client';
 import csrf from '~/lib/utils/csrf';
 
-export default (clientState = {}) =>
+export default (resolvers = {}) =>
   new ApolloClient({
-    uri: `${gon.relative_url_root}/api/graphql`,
-    headers: {
-      [csrf.headerKey]: csrf.token,
-    },
-    clientState,
+    link: createUploadLink({
+      uri: `${gon.relative_url_root}/api/graphql`,
+      headers: {
+        [csrf.headerKey]: csrf.token,
+      },
+    }),
+    cache: new InMemoryCache(),
+    resolvers,
   });
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index 3290f29530a6b..5f6123b5f9b6c 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -46,7 +46,9 @@ Read more about [Vue Apollo][vue-apollo] in the [Vue Apollo documentation][vue-a
 ### Local state with `apollo-link-state`
 
 It is possible to use our Apollo setup with [apollo-link-state][apollo-link-state] by passing
-in the client state object when creating the default client.
+in a resolvers object when creating the default client. The default state can be set by writing
+to the cache after setting up the default client.
+
 
 ```javascript
 import Vue from 'vue';
@@ -54,15 +56,23 @@ import VueApollo from 'vue-apollo';
 import createDefaultClient from '~/lib/graphql';
 Vue.use(VueApollo);
 
+const defaultClient = createDefaultClient({
+  Query: {
+    ...
+  },
+  Mutations: {
+    ...
+  },
+});
+
+defaultClient.cache.writeData({
+  data: {
+    isLoading: true,
+  },
+});
+
 const apolloProvider = new VueApollo({
-  defaultClient: createDefaultClient({
-    defaults: {
-      testing: true,
-    },
-    resolvers: {
-      ...
-    },
-  }),
+  defaultClient,
 });
 ```
 
diff --git a/ee/app/assets/javascripts/design_management/graphql.js b/ee/app/assets/javascripts/design_management/graphql.js
index 6287df2e20aca..36e30ccd5341b 100644
--- a/ee/app/assets/javascripts/design_management/graphql.js
+++ b/ee/app/assets/javascripts/design_management/graphql.js
@@ -28,31 +28,38 @@ const designsStore = [
 ];
 
 const defaultClient = createDefaultClient({
-  defaults: {
-    designs: designsStore,
-  },
-  resolvers: {
-    Query: {
-      design(ctx, { id }) {
-        return designsStore.find(design => design.id === id);
-      },
+  Query: {
+    design(ctx, { id }) {
+      return designsStore.find(design => design.id === id);
     },
-    Mutation: {
-      uploadDesign(ctx, { name }, { cache }) {
-        const designs = name.map(n => ({
-          ...createMockDesign(_.uniqueId()),
-          name: n,
-          commentsCount: 0,
-        }));
+  },
+  Mutation: {
+    uploadDesign(ctx, { files }, { cache }) {
+      const previousDesigns = cache.readQuery({ query: allDesigns });
+      const designs = Array.from(files).map(n => ({
+        ...createMockDesign(_.uniqueId()),
+        name: n.name,
+        commentsCount: 0,
+      }));
+      const data = {
+        designs: designs.concat(previousDesigns.designs),
+      };
+
+      designsStore.unshift(...designs);
 
-        cache.writeData({ data: designs });
+      cache.writeQuery({ query: allDesigns, data });
 
-        return designs;
-      },
+      return designs;
     },
   },
 });
 
+defaultClient.cache.writeData({
+  data: {
+    designs: designsStore,
+  },
+});
+
 defaultClient
   .watchQuery({
     query: allDesigns,
diff --git a/ee/app/assets/javascripts/design_management/pages/design/index.vue b/ee/app/assets/javascripts/design_management/pages/design/index.vue
index e23bebf628072..c203a13aab86b 100644
--- a/ee/app/assets/javascripts/design_management/pages/design/index.vue
+++ b/ee/app/assets/javascripts/design_management/pages/design/index.vue
@@ -1,4 +1,6 @@
 <script>
+import createFlash from '~/flash';
+import { s__ } from '~/locale';
 import Toolbar from '../../components/toolbar/index.vue';
 import DesignImage from '../../components/image.vue';
 import getDesignQuery from '../../queries/getDesign.graphql';
@@ -27,6 +29,12 @@ export default {
           id: this.id,
         };
       },
+      result({ data }) {
+        if (!data) {
+          createFlash(s__('DesignManagement|Could not find design, please try again.'));
+          this.$router.push('/designs');
+        }
+      },
     },
   },
   computed: {
diff --git a/ee/app/assets/javascripts/design_management/pages/index.vue b/ee/app/assets/javascripts/design_management/pages/index.vue
index 487678c924db2..c3ae667d1c6f1 100644
--- a/ee/app/assets/javascripts/design_management/pages/index.vue
+++ b/ee/app/assets/javascripts/design_management/pages/index.vue
@@ -63,7 +63,7 @@ export default {
     onUploadDesign(files) {
       if (!this.canCreateDesign) return null;
 
-      const optimisticResponse = [...files].map(file => ({
+      const optimisticResponse = Array.from(files).map(file => ({
         __typename: 'Design',
         id: -1,
         image: '',
@@ -78,14 +78,15 @@ export default {
         .mutate({
           mutation: uploadDesignQuery,
           variables: {
-            name: [...files].map(({ name }) => name),
+            files,
           },
-          update: (store, { data: { uploadDesign } }) => {
-            const data = store.readQuery({ query: allDesignsQuery });
+          // update: (store, { data: { uploadDesign } }) => {
+          //   const data = store.readQuery({ query: allDesignsQuery });
+          //   console.log(data, uploadDesign);
 
-            data.designs.unshift(...uploadDesign);
-            store.writeQuery({ query: allDesignsQuery, data });
-          },
+          //   data.designs.unshift(...uploadDesign);
+          //   store.writeQuery({ query: allDesignsQuery, data });
+          // },
           optimisticResponse: {
             __typename: 'Mutation',
             uploadDesign: optimisticResponse,
diff --git a/ee/app/assets/javascripts/design_management/queries/uploadDesign.graphql b/ee/app/assets/javascripts/design_management/queries/uploadDesign.graphql
index 16f23c0d4d352..d3c145fb20548 100644
--- a/ee/app/assets/javascripts/design_management/queries/uploadDesign.graphql
+++ b/ee/app/assets/javascripts/design_management/queries/uploadDesign.graphql
@@ -1,5 +1,5 @@
-mutation addDesigns($name: [String]) {
-  uploadDesign(name: $name) @client {
+mutation addDesigns($files: [Upload!]!) {
+  uploadDesign(files: $files) @client {
     id
     image
     name
diff --git a/ee/app/assets/javascripts/design_management/router.js b/ee/app/assets/javascripts/design_management/router.js
index 385dbbe7b9236..fbf660ab954a2 100644
--- a/ee/app/assets/javascripts/design_management/router.js
+++ b/ee/app/assets/javascripts/design_management/router.js
@@ -32,6 +32,15 @@ const router = new VueRouter({
           meta: {
             el: 'designs',
           },
+          beforeEnter(
+            {
+              params: { id },
+            },
+            from,
+            next,
+          ) {
+            if (id !== -1) next();
+          },
           props: ({ params: { id } }) => ({ id: parseInt(id, 10) }),
         },
       ],
diff --git a/ee/spec/frontend/design_management/pages/index_spec.js b/ee/spec/frontend/design_management/pages/index_spec.js
index a78fcaebf5e33..1bc0743fe3488 100644
--- a/ee/spec/frontend/design_management/pages/index_spec.js
+++ b/ee/spec/frontend/design_management/pages/index_spec.js
@@ -75,9 +75,8 @@ describe('Design management index page', () => {
         .then(() => {
           expect(mutate).toHaveBeenCalledWith({
             mutation: uploadDesignQuery,
-            update: expect.any(Function),
             variables: {
-              name: ['test'],
+              files: [{ name: 'test' }],
             },
             optimisticResponse: {
               __typename: 'Mutation',
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 27dd698af86d5..d3d20b1689947 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3520,6 +3520,9 @@ msgstr ""
 msgid "Description:"
 msgstr ""
 
+msgid "DesignManagement|Could not find design, please try again."
+msgstr ""
+
 msgid "DesignManagement|Error uploading a new design. Please try again"
 msgstr ""
 
diff --git a/package.json b/package.json
index 547111d7b44f4..d672370e9a3ec 100644
--- a/package.json
+++ b/package.json
@@ -34,8 +34,9 @@
     "@gitlab/csslab": "^1.9.0",
     "@gitlab/svgs": "^1.57.0",
     "@gitlab/ui": "^3.0.0",
-    "apollo-boost": "^0.3.1",
+    "apollo-cache-inmemory": "^1.5.1",
     "apollo-client": "^2.5.1",
+    "apollo-upload-client": "^10.0.0",
     "at.js": "^1.5.4",
     "autosize": "^4.0.0",
     "axios": "^0.17.1",
diff --git a/yarn.lock b/yarn.lock
index 970be4c774aaf..61f2736cab87c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1114,21 +1114,6 @@ anymatch@^2.0.0:
     micromatch "^3.1.4"
     normalize-path "^2.1.1"
 
-apollo-boost@^0.3.1:
-  version "0.3.1"
-  resolved "https://registry.yarnpkg.com/apollo-boost/-/apollo-boost-0.3.1.tgz#b6a896e020a0eab7e415032fe565734a955c65f8"
-  integrity sha512-VdXcTMxLBeNvANW/FtiarEkrRr/cepYKG6wTAURdy8CS33WYpEHtIg9S8tAjxwVzIECpE4lWyDKyPLoESJ072Q==
-  dependencies:
-    apollo-cache "^1.2.1"
-    apollo-cache-inmemory "^1.5.1"
-    apollo-client "^2.5.1"
-    apollo-link "^1.0.6"
-    apollo-link-error "^1.0.3"
-    apollo-link-http "^1.3.1"
-    graphql-tag "^2.4.2"
-    ts-invariant "^0.2.1"
-    tslib "^1.9.3"
-
 apollo-cache-inmemory@^1.5.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.5.1.tgz#265d1ee67b0bf0aca9c37629d410bfae44e62953"
@@ -1170,29 +1155,16 @@ apollo-link-dedup@^1.0.0:
   dependencies:
     apollo-link "^1.2.3"
 
-apollo-link-error@^1.0.3:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/apollo-link-error/-/apollo-link-error-1.1.1.tgz#69d7124d4dc11ce60f505c940f05d4f1aa0945fb"
-  integrity sha512-/yPcaQWcBdB94vpJ4FsiCJt1dAGGRm+6Tsj3wKwP+72taBH+UsGRQQZk7U/1cpZwl1yqhHZn+ZNhVOebpPcIlA==
+apollo-link-http-common@^0.2.8:
+  version "0.2.13"
+  resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.13.tgz#c688f6baaffdc7b269b2db7ae89dae7c58b5b350"
+  integrity sha512-Uyg1ECQpTTA691Fwx5e6Rc/6CPSu4TB4pQRTGIpwZ4l5JDOQ+812Wvi/e3IInmzOZpwx5YrrOfXrtN8BrsDXoA==
   dependencies:
-    apollo-link "^1.2.3"
-
-apollo-link-http-common@^0.2.5:
-  version "0.2.5"
-  resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.5.tgz#d094beb7971523203359bf830bfbfa7b4e7c30ed"
-  integrity sha512-6FV1wr5AqAyJ64Em1dq5hhGgiyxZE383VJQmhIoDVc3MyNcFL92TkhxREOs4rnH2a9X2iJMko7nodHSGLC6d8w==
-  dependencies:
-    apollo-link "^1.2.3"
-
-apollo-link-http@^1.3.1:
-  version "1.5.5"
-  resolved "https://registry.yarnpkg.com/apollo-link-http/-/apollo-link-http-1.5.5.tgz#7dbe851821771ad67fa29e3900c57f38cbd80da8"
-  integrity sha512-C5N6N/mRwmepvtzO27dgMEU3MMtRKSqcljBkYNZmWwH11BxkUQ5imBLPM3V4QJXNE7NFuAQAB5PeUd4ligivTQ==
-  dependencies:
-    apollo-link "^1.2.3"
-    apollo-link-http-common "^0.2.5"
+    apollo-link "^1.2.11"
+    ts-invariant "^0.3.2"
+    tslib "^1.9.3"
 
-apollo-link@^1.0.0, apollo-link@^1.0.6, apollo-link@^1.2.3:
+apollo-link@^1.0.0, apollo-link@^1.2.3:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.3.tgz#9bd8d5fe1d88d31dc91dae9ecc22474d451fb70d"
   integrity sha512-iL9yS2OfxYhigme5bpTbmRyC+Htt6tyo2fRMHT3K1XRL/C5IQDDz37OjpPy4ndx7WInSvfSZaaOTKFja9VWqSw==
@@ -1200,6 +1172,25 @@ apollo-link@^1.0.0, apollo-link@^1.0.6, apollo-link@^1.2.3:
     apollo-utilities "^1.0.0"
     zen-observable-ts "^0.8.10"
 
+apollo-link@^1.2.11, apollo-link@^1.2.6:
+  version "1.2.11"
+  resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.11.tgz#493293b747ad3237114ccd22e9f559e5e24a194d"
+  integrity sha512-PQvRCg13VduLy3X/0L79M6uOpTh5iHdxnxYuo8yL7sJlWybKRJwsv4IcRBJpMFbChOOaHY7Og9wgPo6DLKDKDA==
+  dependencies:
+    apollo-utilities "^1.2.1"
+    ts-invariant "^0.3.2"
+    tslib "^1.9.3"
+    zen-observable-ts "^0.8.18"
+
+apollo-upload-client@^10.0.0:
+  version "10.0.0"
+  resolved "https://registry.yarnpkg.com/apollo-upload-client/-/apollo-upload-client-10.0.0.tgz#6cc3d0ea2aef40bc237b655f5042809cacee1859"
+  integrity sha512-N0SENiEkZXoY4nl9xxrXFcj/cL0AVkSNQ4aYXSaruCBWE0aKpK6aCe4DBmiEHrK3FAsMxZPEJxBRIWNbsXT8dw==
+  dependencies:
+    apollo-link "^1.2.6"
+    apollo-link-http-common "^0.2.8"
+    extract-files "^5.0.0"
+
 apollo-utilities@1.2.1, apollo-utilities@^1.0.0, apollo-utilities@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.2.1.tgz#1c3a1ebf5607d7c8efe7636daaf58e7463b41b3c"
@@ -4134,6 +4125,11 @@ extglob@^2.0.4:
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
+extract-files@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-5.0.1.tgz#c9492a8410be643e260a376f0151361993d5f659"
+  integrity sha512-qRW6y9eKF0VbCyOoOEtFhzJ3uykAw8GKwQVXyAIqwocyEWW4m+v+evec34RwtUkkxxHh7NKBLJ6AnXM8W4dH5w==
+
 extract-from-css@^0.4.4:
   version "0.4.4"
   resolved "https://registry.yarnpkg.com/extract-from-css/-/extract-from-css-0.4.4.tgz#1ea7df2e7c7c6eb9922fa08e8adaea486f6f8f92"
@@ -4778,7 +4774,7 @@ graphlibrary@^2.2.0:
   dependencies:
     lodash "^4.17.5"
 
-graphql-tag@^2.10.0, graphql-tag@^2.4.2:
+graphql-tag@^2.10.0:
   version "2.10.0"
   resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.0.tgz#87da024be863e357551b2b8700e496ee2d4353ae"
   integrity sha512-9FD6cw976TLLf9WYIUPCaaTpniawIjHWZSwIRZSjrfufJamcXbVVYfN2TWvJYbw0Xf2JjYbl1/f2+wDnBVw3/w==
@@ -10460,6 +10456,13 @@ ts-invariant@^0.2.1:
   dependencies:
     tslib "^1.9.3"
 
+ts-invariant@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.3.2.tgz#89a2ffeb70879b777258df1df1c59383c35209b0"
+  integrity sha512-QsY8BCaRnHiB5T6iE4DPlJMAKEG3gzMiUco9FEt1jUXQf0XP6zi0idT0i0rMTu8A326JqNSDsmlkA9dRSh1TRg==
+  dependencies:
+    tslib "^1.9.3"
+
 ts-jest@24.0.0, ts-jest@^23.10.5:
   version "24.0.0"
   resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.0.0.tgz#3f26bf2ec1fa584863a5a9c29bd8717d549efbf6"
@@ -11540,6 +11543,14 @@ zen-observable-ts@^0.8.10:
   dependencies:
     zen-observable "^0.8.0"
 
+zen-observable-ts@^0.8.18:
+  version "0.8.18"
+  resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.18.tgz#ade44b1060cc4a800627856ec10b9c67f5f639c8"
+  integrity sha512-q7d05s75Rn1j39U5Oapg3HI2wzriVwERVo4N7uFGpIYuHB9ff02P/E92P9B8T7QVC93jCMHpbXH7X0eVR5LA7A==
+  dependencies:
+    tslib "^1.9.3"
+    zen-observable "^0.8.0"
+
 zen-observable@^0.8.0:
   version "0.8.11"
   resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.11.tgz#d3415885eeeb42ee5abb9821c95bb518fcd6d199"
-- 
GitLab