[SOLVED] REST API CORS error

I’m trying to get an asset through the REST API, but I am getting a CORS error. Is there anything that I’m doing wrong?

const token = ...;
const assetId = "27532735";
const url = `https://playcanvas.com/api/assets/${assetId}/file`;

$(document).ready(function(){
    $('#load').click(function(){
        $.ajax({
            url: url,
            type: "GET",
            headers: {
                "Authorization": "Bearer " + token
            },
            success: function(result){
                console.log(result);
            },
            error: function(error){
                console.error(error);
            }
        })
    });
});

Hi @Ivolutio,

To get the correct url with all of the access tokens attached you need to use this method:

const assetId = "27532735";
const asset = this.app.assets.get(assetId);
const assetUrl = asset.getFileUrl();

If you are running through the editor, then that will be a relative url, and you may need to an additional check and append: https://playcanvas.com/

Oh, so it is not possible to get a file outside PlayCanvas? I am looking into ways to have artists develop in the editor itself but then export scene data (and probably assets) to our own database.

It’s possible using a specific route, you will have to:

  1. Publish a build from the editor
  2. Grab the build url.
  3. Use a GET url to a special url to grab the config.json from the published scene. That file includes all scene assets and their remote url.

Here is some typescript code on how I do this on a local project of mine.

SceneID is extracted from the build url. For example if this is your build url:

https://playcanv.as/b/0BOezsbc/

Then the sceneID is 0BOezsbc. Also note this code is unofficial and all urls are subject to change.

  static async loadPlaycanvasScene(sceneID: string) {
    const playcanvasServerUrl: string =
      "https://s3-eu-west-1.amazonaws.com/apps.playcanvas.com/";

    // --- load config
    const configUrl: string = playcanvasServerUrl + sceneID + "/config.json";
    const config = await Request.fetchJSON(configUrl);

    // --- prepare urls
    const assetsUrl: string = playcanvasServerUrl + sceneID + "/";

    // --- prepare remote assets for loading
    for (var assetID in config.assets) {
      var asset = config.assets[assetID];

      if (asset && asset.file && asset.file.url) {
        asset.file.url = assetsUrl + asset.file.url;
      }
    }

    // config.assets now holds assets with a full remote url
  }
1 Like

Ok then that will work for now. Still would be amazing if we could directly interface our projects :slight_smile: #suggestions

1 Like

I think I missed that you were using the Rest API with the correct access token in the headers.

Your initial code should work as well, for grabbing assets directly with their asset ID. It shouldn’t be throwing a CORS error.

Invoking @vaios @yaustar they may be able to check on this.

The request looks fine, are you doing this from another site so there’s a different domain? I’m guessing thats not permitted and the REST API is for server/local use.

What’s the use case if so?

The goal will be to interface assets from our project (and scene data) from our upload dashboard to our database. I don’t want to get into the very specific goals with this, but we right now load standalone 3d models in a viewer, but we want to expand our system to allow extra configuration through the playcanvas editor instead of rebuilding a similar editor.

So this script runs on my localhost, but will be run on our own domain later.

I guess this won’t stop us from setting something up like this, as it is possible to export data ourselves anyway by e.g. adding a button in the playcanvas hosted build.

Ah, that’s interesting! So effectively, you are making a tool out of the PlayCanvas editor?

We are looking to make the Editor API public and standardised where users can make their own Editor extensions for project based custom tooling.

1 Like

Yes! Right now I have created a upload dashboard where you can select model, textures and configure camera rotation and zoom which will then be pushed to our database. I could keep adding features to this dashboard but might be more efficient to use the existing editor - a public API / extentions would be amazing :smiley:

We are interested in both working more closely with you for this and testing, if that’s helpful. Contact: ivo@digitalchef.eu

1 Like

@yaustar so from reading other forum posts, I get the API isn’t considered public 100%? I am looking at the webpack plugin from @whydoidoit (https://github.com/whydoidoit/playcanvas-webpack-plugin/blob/master/index.js) and it uses the API to update a file. Now I am trying to expand this code to use a branchId property but the API doesn’t seem to update it on the other branch.

My extra line of code:

let req = request({
    uri: `https://playcanvas.com/api/assets`,
    method: 'POST',
    headers: {
        "Authorization": `Bearer ${options.bearer}`
    }
})
let form = req.form()
form.append("project", "" + options.project)
form.append("name", "" + filename.path)
form.append("branchId", "" + options.branchId) //this doesnt work
form.append("asset", "" + filename.assetId)
form.append("data", JSON.stringify({order: filename.priority || 100, scripts: {}}))
form.append("preload", "true")
form.append("file", content, {
    filename: filename.path,
    contentType: "text/javascript"
})
req.then((res) => {
    console.log("Upload complete for file " + filename.path)
    callback()
}, (e) => {
    console.error(e)
    compilation.errors.push(e)
    callback()
})

Is there a undocumented change that could make this work?

The REST API is public https://developer.playcanvas.com/en/user-manual/api/

The Editor API isn’t ‘public’ but available to use if you are willing to dig through the editor code.

The branchId might either be the name of the branch or the really long GUID
image

But this code is all using the REST API right? It looks like https://developer.playcanvas.com/en/user-manual/api/asset-create/

I just tried POSTing to my branch ‘test’, but then it gives ERROR in 404 - "{\"error\":\"Branch test not found\"}". I am also positive I have to give the GUID, as it returns an object on a successful request:

{
    "id": ...,
    "uniqueId": ....,
    "createdAt": "2020-06-07T19:52:59.599Z",
    "modifiedAt": "2020-06-07T10:59:32.502Z",
    "file": {
        "filename": "main.build.js",
        "size": 109189,
        "hash": "8f024c2566f6488374e93e775772c815",
        "url": "/api/assets/.../file/main.build.js?branchId=masterSceneGUID" //this here
    },
    "name": "main.build.js",
    "type": "script",
    "scope": {
        "type": "project",
        "id": ...
    },
    "source": false,
    "tags": [],
    "preload": true,
    "data": {
        "order": 100,
        "scripts": {
            ...
            }
        },
        "loadingType": 0,
        "loading": false
    },
    "i18n": {},
    "sourceId": null,
    "parent": null
}

When you create your access token, are you using the ORG account or the User account?

I followed the initial guide for the webpack plugin, which said to get the editor access token. So I now tried using an organisation token and rewrote the plugin to use the general API.

Tada :stuck_out_tongue:

let req = request({
    uri: `https://playcanvas.com/api/assets/${filename.assetId}`,
    method: 'PUT',
    headers: {
        "Authorization": `Bearer ${options.bearer}`
    }
})
let form = req.form()
form.append("branchId", "" + options.branchId)
form.append("file", content, {
    filename: filename.path,
    contentType: "text/javascript"
})
req.then(() => {
    console.log("Upload complete for file (update) " + filename.path)
    callback()
}, (e) => {
    if(e.statusCode === 404){
        let req = request({
            uri: `https://playcanvas.com/api/assets`,
            method: 'POST',
            headers: {
                "Authorization": `Bearer ${options.bearer}`
            }
        })
        let form = req.form()
        form.append("name", "" + filename.path)
        form.append("project", "" + options.project)
        form.append("branchId", "" + options.branchId)
        form.append("preload", "true")
        form.append("file", content, {
            filename: filename.path,
            contentType: "text/javascript"
        })
        req.then((res) => {
            console.log("Upload complete for file (create) " + filename.path)
            compilation.warnings.push("PlayCanvas Webpack Plugin\nNew main.build.js has been created. Update fileId config to" + JSON.parse(res).id)
            callback()
        }, (e) => {
            console.error(e)
            compilation.errors.push(e)
            callback()
        })
    }else{
        console.error(e)
        compilation.errors.push(e)
        callback()
    }
})
2 Likes

Good!
If you want to use other APIs
use it if you want. ↓

2 Likes