Have your say in Community Polls: What was/is your greatest motivation to start your own business?
Notice: We've implemented a new Partners and Developers board structure and moved content into the new structure. To find your previously submitted topics and replies click on your avatar in the top right followed by My Profile.
Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

Re: Cannot upload .glb 3d model via graphql

Cannot upload .glb 3d model via graphql

twilson90
Shopify Partner
32 0 23

Don't have this problem with uploading video or images, but for some reason it's not allowing me to upload 3d models with a .glb extension.

 

I successfully uploaded the file in the staging process, then when it comes to using 'mutation fileCreate' in the 2023-07 API, I get the following error:

 
"Provided filename extension must match original source."
 
However, when I check the originalSource and the
filename in the 
FileCreateInput, they have identical extensions:
filename: "Elephant_Plushie.glb"
 
I'm wondering if the URL parameters in the originalSource url are not taken into consideration.
That's bug #1.
 
I remove the filename field, then it appears to successfully upload with no errors.
But then it remains processing for several minutes before failing.
 According to the documentation there should be nothing wrong with this FileCreateInput:
 

However it's just not working. I noticed the storage.googleapis
 URL when clicked is returning a 403 error code, but I assume the Shopify server when fetching the same URL does not have this issue.
 
Nothing is invalid with my 3D model, if I upload it using the Shopify Admin UI it uploads and is processed almost immediately.
 
Pretty sure these are a couple of bugs but there's nowhere to report them.
Would appreciate hearing from anyone who has successfully managed to upload a 3D model via graphql.
 
EDIT:
 
I finally found a way around it. The problem (aka BUG) is in declaring the file 'resource' as 'MODEL_3D' like the documentation suggests.
This simply will not work, unless maybe the 3d model will be attached to a product.
So I set the resource to 'FILE' and the stagedUploadsCreate response gave me a URL that actually worked.
If in fact it was my mistake, and setting resource to 'MODEL_3D' will only work when using createProductMedia, then that is completely unintuitive and needs explaining in the documentation, and some helpful errors responses wouldn't go amiss either.
 
Shopify's API is a nightmare sometimes...
Replies 3 (3)

Xen-dev
Shopify Partner
16 0 1

in stagedUploadsCreate how did you define the mutation in GraphQL especially the filename of the model?

 

twilson90
Shopify Partner
32 0 23

 

 

var file_inputs = [];
    var upload_inputs = [];
    var contents = [];
    for (var data of datas) {
      var filename = path.basename(data.filename);
      var stat = null;
      var resource = data.resource || ShopifyWrapper.UPLOAD_TARGET_RESOURCE_TYPES.FILE;
      try { stat = fs.statSync(data.filename); } catch {}
      var mimetype = data.mimetype || mime.lookup(data.filename) || "";
      var content_type = mimetype.split("/")[0].toUpperCase();
      if (!["FILE","IMAGE","VIDEO"].includes(content_type)) content_type = "FILE";
      var file_input = {
        "filename": filename,
        "alt": data.alt,
        "contentType": content_type,
      }
      var content = data.content || (await fs.readFile(data.filename));
      if (content instanceof Readable) content = await streamToBuffer(content);
      var size = data.size;
      if (!size && content) content.length
      var upload_input = {
        "filename": filename,
        "mimeType": mimetype,
        "resource": resource,
        "fileSize": size,
        "httpMethod": "POST",
      }

      contents.push(content);
      file_inputs.push(file_input);
      upload_inputs.push(upload_input);
    }
    if (upload_inputs.length) {
      var response = await this.graphql(
        `mutation stagedUploadsCreate($input: [StagedUploadInput!]!) {
          stagedUploadsCreate(input: $input) {
            stagedTargets {
              url
              resourceUrl
              parameters { name, value }
            }
            userErrors {
              field, message
            }
          }
        }`, {
          input: upload_inputs
        }
      );
      if (response.stagedUploadsCreate.userErrors.length) console.error(response.stagedUploadsCreate.userErrors);

      for (var i = 0; i < file_inputs.length; i++) {
        var file_input = file_inputs[i];
        var content = contents[i];
        var t = response.stagedUploadsCreate.stagedTargets[i];
        file_input.originalSource = t.resourceUrl;
        var form_data = new FormData();
        for (var p of t.parameters) {
          form_data.append(p.name, p.value);
        }
        form_data.append("file", content, {filename:file_input.filename});
        var res = await axios.post(t.url, form_data);
      }
    }
    return file_inputs;

 

 

^ This is the code I used. The bit you probably need to  know about is how to define each StagedUploadInput:

 

var upload_input = {
        "filename": filename,
        "mimeType": mimetype,
        "resource": resource,
        "fileSize": size,
        "httpMethod": "POST",
      }

 

 

You use that to define each file you're about to upload and it has to be correct. For 3d models (or any file type) the 'resource' field has to equal "FILE" if you are just uploading to the 'Files' section essentially. It's a very strange and intolerant system!
The response contains a special url that you can then upload to. Then you make a request to that url for each file using the httpMethod that you stated earlier, attaching each file as form data.

Xen-dev
Shopify Partner
16 0 1

I was able the generate the resourceUrl and upon logging it, but its giving me an error saying no such key exists, I am thinking that there might be an issue with only the filename I am calling, 

 

 

 

    const stagedUploadsResponse = await admin!.graphql(
      `#graphql
      mutation StagedUploadsCreate($input: [StagedUploadInput!]!) {
        stagedUploadsCreate(input: $input) {
          stagedTargets {
            url
            resourceUrl
            parameters {
              name
              value
            }
          }
        }
      }`,
      {
        variables: {
          input: [
            {
              filename: "watch.glb",
              mimeType: "model/gltf-binary",
              resource: "FILE",
              fileSize: "3846680",
              httpMethod: "POST",
            },
          ],
        },
      },
    );

 

Screenshot 2024-03-07 at 11.09.08 AM.png