Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No UI example found #29

Open
amal-chandran opened this issue Mar 1, 2019 · 8 comments
Open

No UI example found #29

amal-chandran opened this issue Mar 1, 2019 · 8 comments

Comments

@amal-chandran
Copy link

We are trying to use this in our project not able to create a ui not even properly test.we need an documentation/ examples including ui

@GasimGasimzada
Copy link

What do you mean by UI example?

@amal-chandran
Copy link
Author

There is no client side code in the example only backend code is there. A React.JS implementation is missing in the example.

@GasimGasimzada
Copy link

This is a backend implementation for GraphQL multipart specification. There separate frontend repositories for different frameworks and libraries. Here is the repo for the specification: https://github.com/jaydenseric/graphql-multipart-request-spec/blob/master/readme.md. If you check the end of README, you will see different repos for frameworks / libraries for both client and server.

I think it doesn’t make sense to show examples for frontend here because it is based on a specification written by a 3rd party. Plus, If React example is added, Angular and Vue examples should also be added. The specification’s repo has all the necessary information for different tools.

@luiskarlos
Copy link

luiskarlos commented Jun 7, 2022

for those who are still struggling with this (as I did) here is a code that may help
Please be aware of the endpoint URL, which has to be adjusted to your needs.

import { print } from 'graphql/language/printer';

const createOptions = (opts) => {
  const formData = new FormData();

  opts.variables.files.forEach((file, i) => {
    formData.append('map', `{"${i}": ["variables.files.${i}"] }`);
    formData.append(`${i}`, file, file.name);
  });

  const files_null = opts.variables.files.map(() => null) // one null for each file, e.g. "null,null,null"
  const variables = {...opts.variables}
  variables.files = files_null

  //see https://stackoverflow.com/questions/57490569/import-graphql-query-gql-file-into-plain-javascript-and-run-an-axios-call
  const value = {
    query: print(opts.query),
    variables: variables
  }
  formData.append('operations', JSON.stringify(value).replace(/\\n/g, ''))

  return {
    method: 'POST',
    credentials: 'include',
    body: formData,
  }
}

const uploadFile = (opts) => {
  console.assert(opts.variables.files.length > 0, 'files is empty');
  return fetch(
    `${process.env.NEXT_PUBLIC_BACKEND}upload/`,
    createOptions(opts)
  );
};

// const [uploadFiles, { status }] = useUploadFiles()
export default uploadFile;

Mutation

export const UPLOAD_FILES = gql`
  mutation uploadFiles($files: [Upload]!) {
    uploadFiles(files: $files) {
      ok
      urls {
        url
        name
      }
    }
  }
`

Call: just use an input type="file" and call

uploadFIle({query: UPLOAD_FILES, variables: { files: [e.target.files[0]] }}

@boomdizz
Copy link

boomdizz commented Dec 8, 2022

@luiskarlos I am doing the same thing, but getting an empty dict on the server (Django/graphene) side. Also in that last line you posted, it should be 'mutation: UPLOAD_FILES' rather than 'query: UPLOAD_FILES', right?
Here is my (failing) code. Perhaps you (or someone else) can spot something obvious:

const UPLOAD_NEW_FILE = gql `
mutation UploadNewFile($fileToUpload: Upload!) {
  uploadNewFile(file: $fileToUpload) {
    success
  }
}`;

  uploadFile(agFile: File): Observable<FetchResult<any>>  {
    return this.apollo.mutate({
      mutation: UPLOAD_NEW_FILE,
      variables: {
        fileToUpload: agFile
      }
    });
  

And I am calling this method like this:

  onFileUpload(event) {
    const myFile = event.target.files[0]
    this.configSvc.uploadFile(myFile).subscribe(({ data }) => {
      console.log("Upload of package was " + data.success)
    })
  }

onFileUpload is invoked from an 'input type="file" HTML element. I have printed (console.log) the myFile variable in onFileUpload handler function and the 'agFile' variable in the uploadFile service function right before the mutation call, and they indeed are file objects. But for whatever reason, when apollo picks it up it makes it into an empty dict.

Any help is much appreciated.

@boomdizz
Copy link

boomdizz commented Dec 8, 2022

Also adding that the above mutation is working from the Altair graphql client.

@luiskarlos
Copy link

luiskarlos commented Dec 10, 2022

@boomdizz This is the code I am using right now, my backend is with python so I am not sure how it will work with js:

this is a utility uploadFile.js

import { print } from 'graphql/language/printer';

const createOptions = (opts) => {
  const formData = new FormData();

  opts.variables.files.forEach((file, i) => {
    formData.append('map', `{"${i}": ["variables.files.${i}"] }`);
    formData.append(`${i}`, file, file.name);
  });

  const files_null = opts.variables.files.map(() => null); // one null for each file, e.g. "null,null,null"
  const variables = { ...opts.variables };
  variables.files = files_null;

  //see https://stackoverflow.com/questions/57490569/import-graphql-query-gql-file-into-plain-javascript-and-run-an-axios-call
  const value = {
    query: print(opts.query),
    variables: variables,
  };
  formData.append('operations', JSON.stringify(value).replace(/\\n/g, ''));

  return {
    method: 'POST',
    credentials: 'include',
    body: formData,
  };
};

const uploadFile = (opts) => {
  console.assert(opts.variables.files.length > 0, 'files is empty');
  return fetch(
    `${process.env.NEXT_PUBLIC_BACKEND}upload/`,
    createOptions(opts)
  );
};

export default uploadFile;

This a hook

import { useEffect, useState } from 'react';
import uploadFile from '../utils/uploadFile';

const useUpload = (query) => {
  const [options, upload] = useState({ variables: {} });
  const [status, setStatus] = useState({
    uploading: false,
    success: false,
    error: false,
    data: null,
    message: null,
  });

  const updateStatus = (uploading, success, error, data, message) =>
    setStatus({ uploading, success, error, data, message });

  useEffect(() => {
    if (options.variables?.files?.length) {
      updateStatus(true, false, false, null, 'Uploading...');

      uploadFile({ query, ...options })
        .then((result) => {
          return result.json();
        })
        .then((result) => {
          if (result.errors) {
            throw new Error(result.errors[0].message);
          }

          updateStatus(
            false,
            true,
            false,
            result.data,
            'Files Uploaded Successfully'
          );
        })
        .catch((error) => {
          updateStatus(false, false, true, error.message);
          console.log('error', error);
        });
    }
  }, [options]);

  return [upload, { ...status }];
};

// const [uploadFiles, { status }] = useUploadFiles()
export default useUpload;

This is the query:

export const UPLOAD_FILES = gql`
  mutation uploadFiles($files: [Upload]!) {
    uploadFiles(files: $files) {
      ok
    }
  }
`;

This is my mutation:

class UploadFilesMutation(graphene.Mutation):

    class Arguments:
        files = graphene.List(Upload)

    file = graphene.Field(FileType)

    def mutate(self, info, files,  **kwargs):
        rf = StorageSystem.save_file(info.context.user, files[0], public=False)
        return UploadFilesMutation(document=document)

@boomdizz
Copy link

I was able to finally get it to work in the Angular/Apollo environment. I have it documented as the answer to my own question on Stackoverflow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants