Proper way for call api with custom filters from create/update script

I’m submitting a…

  • Regression (a behavior that stopped working in a new release)
  • Bug report
  • Performance issue
  • Documentation issue or request

Current behavior

According the documentation https://docs.squidex.io/02-documentation/developer-guides/scripting there is a function for to fetch content from the CMS by ID or IDs.

There is also a function getJSON/postJSON for fetch data from external API.

I have no bearer access_token on user in context [ctx] (like I have in the sidebar/editor plugin) which would give me the ability to make an authenticated call to the CMS for to fetch data by custom filters (like in Postman or from sidebar/editor HTML)

Expected behavior

Description of how to call the CMS with provided context information.
-or-
Function or extension of getReferences for to fetch content with custom filter

Minimal reproduction of the problem

Actual content of the provided context [ctx] in the update script:
(AppName, E-Mail and Host changed)

{
  "data": { ... },
  "dataOld": { ... },
  "oldData": { ... },
  "oldStatus": "Published",
  "operation": "Update",
  "status": "Published",
  "statusOld": "Unknown",
  "appId": "85ca01a5-01c5-4c10-b6a4-65760bfa045c",
  "appName": "...live",
  "contentId": "d546d720-7650-4597-acad-3d44a1ed0956",
  "schemaId": "15b9e1cf-f423-41f5-8c05-737f0d486d2e",
  "schemaName": "page",
  "user": {
    "id": "6511928064157693fb049329",
    "email": "...info@...ch",
    "isClient": false,
    "isUser": true,
    "name": null,
    "claims": {
      "sub": [
        "6511928064157693fb049329"
      ],
      "name": [
        "...info@...ch"
      ],
      "emailaddress": [
        "...info@...ch"
      ],
      "permissions": [
        "squidex.admin.*",
        "squidex.apps....live.*"
      ],
      "email": [
        "...info@...ch"
      ],
      "oi_prst": [
        "squidex-frontend"
      ],
      "iss": [
        "https://...cloudapp.azure.com/"
      ],
      "oi_au_id": [
        "f88b7faa-78e8-431c-b576-c910a65b6ed4"
      ],
      "client_id": [
        "squidex-frontend"
      ],
      "oi_tkn_id": [
        "d55c2911-0244-4ec3-bcdd-b39f8f4965d1"
      ],
      "aud": [
        "scp:squidex-api"
      ],
      "scope": [
        "squidex-api openid profile email permissions"
      ],
      "jti": [
        "a06eda7d-9da1-4319-a65b-d95108f9e2b5"
      ],
      "exp": [
        "1702645950"
      ],
      "iat": [
        "1700053950"
      ],
      "oi_tkn_typ": [
        "access_token"
      ],
      "oi_crt_dt": [
        "Wed, 15 Nov 2023 13:12:30 GMT"
      ],
      "oi_exp_dt": [
        "Fri, 15 Dec 2023 13:12:30 GMT"
      ],
      "oi_aud": [
        "scp:squidex-api"
      ],
      "oi_scp": [
        "squidex-api",
        "openid",
        "profile",
        "email",
        "permissions"
      ],
      "role": [
        "Owner"
      ]
    }
  }
}

Environment

App Name: …live

  • Self hosted with docker
  • Self hosted with IIS
  • Self hosted with other version
  • Cloud version

Version: 7.8.2

Browser:

  • Chrome (desktop)
  • Chrome (Android)
  • Chrome (iOS)
  • Firefox
  • Safari (desktop)
  • Safari (iOS)
  • IE
  • Edge

Others:

The length of a bearer token which I receive in Postman is 854 and looks like a base64 encoded string - nothing like that found in the update script context.

I may give some example scripts and show what I’m doing there.

In short, we have localized URL-slugs and since the localized fields cannot be set as “unique” i had to check them by myself - so I made a script which checks the existing content for same slug name.

The check will append an increment number “-1” following the last exist number suffix.

Working code (bearer-token fetched manually):

var data = ctx.data;
//data.debug.iv = JSON.stringify({action:'update',context:ctx}, null, 2);
if (data.title) {
    if(data.title.de && (!data.slug || !data.slug.de || data.slug.de == '')){
        data.slug.de = slugify(data.title.de);
    }
    if(data.title.fr && (!data.slug || !data.slug.fr || data.slug.fr == '')){
        data.slug.fr = slugify(data.title.fr);
    }
    if(data.title.it && (!data.slug || !data.slug.it || data.slug.it == '')){
        data.slug.it = slugify(data.title.it);
    }
    if(data.title.en && (!data.slug || !data.slug.en || data.slug.en == '')){
        data.slug.en = slugify(data.title.en);
    }
}

/*var tokenurl = ctx.user.claims.iss + 'identity-server/connect/token';
postJSON(tokenurl, {}, (d) => {
    data.debug.iv
}, headers?, ignoreError?)*/

var instancesStarted = 0;
function finish() {
    if (instancesStarted == 0) {
        replace(data);
    } else {
        //data.debug.iv += ' | wait for started instances to finish; num of instances: ' + instancesStarted;
    }
}

var url;
function checkandreplaceslug(field, lang, id) {
    instancesStarted++;
    var value = field[lang];
    //data.debug.iv = "origin data("+lang+"): " + value;
    var query = {
        "filter": {
            "path": "data.slug." + lang,
            "op": "matchs",
            "value": "^"+value+"(-[0-9]+)?$"
        }
    };
    url = ctx.user.claims.iss + "api/content/" + ctx.appName + "/" + ctx.schemaName + "?q=" + encodeURIComponent(JSON.stringify(query));
    //data.debug.iv += ' | URL: ' + url;
    var bearerToken = 'my-generated-token-fetched-with-postman';
    let authHeader = `Bearer ${bearerToken}`;
    //data.debug.iv += ' | header: ' + authHeader;
    var testdata = getJSON(url, (responseData) => {
        if (responseData && responseData['items']) {
            var numEquals = 0;
            var numEqualsSameId = 0;
            var currentHighestNum = 0;
            var previousSlug = null;
            for(var i = 0, l = responseData['items'].length; i < l; i++) {
                var rId = responseData['items'][i]['id'];
                var rSlug = responseData['items'][i].data.slug[lang];
                // test-title
                // test-title
                // test-title-1
                // test-title-2
                if (rSlug == value) {
                    numEquals++;
                    if (rId == id) {
                        numEqualsSameId++;
                    }
                } else {
                    var rNum = rSlug.split('-').pop();
                    //data.debug.iv += ' | check-num: ' + JSON.stringify({rSlug:rSlug, rNum:rNum, inan:isNaN(rNum)});
                    if (!isNaN(rNum)) {
                        let irNum = rNum * 1;
                        if (irNum > currentHighestNum) {
                            currentHighestNum = irNum;
                        }
                        if (rId == id) {
                            previousSlug = rSlug;
                        }
                    }
                }
                //data.debug.iv += ' | check: ' + JSON.stringify({rSlug:rSlug, eq:(rSlug == value), rId:rId, id_eq:(rId == id)});
            }
            if (numEquals == 0 || (numEquals == 1 && numEqualsSameId == 1)) {
                // everything fine
            } else {
                // apply previous numbered slug for to prevent nonsense upcounting
                if (previousSlug != null) 
                    field[lang] = previousSlug;
                else
                    field[lang] = value + '-' + (currentHighestNum + 1);
            }
            //data.debug.iv += ' | results: ' + JSON.stringify({nE:numEquals,nEsID:numEqualsSameId,cHN:currentHighestNum,nV:field[lang]});
        }
        //data.debug.iv += ' | response: ' + JSON.stringify(responseData);
        instancesStarted--;
        finish();
    }, { Authorization: authHeader});
    //data.debug.iv += ' | testdata: ' + JSON.stringify(testdata);
    //data.debug.iv = 'after getJSON()';
}

try {
    checkandreplaceslug(data.slug, 'de', ctx.contentId);
    checkandreplaceslug(data.slug, 'fr', ctx.contentId);
}
catch(e) {
    //data.debug.iv += JSON.stringify(e);
    //reject('Exception in update script: ' + e + ' (' + url + ')');
}
//data.debug.iv += ' | call finish after try-block: ' + instancesStarted;
finish();