Structr 4.x Migration Guide

The 4.0 release of Structr includes multiple changes to the framework which require a migration of applications that are built with Structr 2.x and 3.x. The following chapters will give an overview of how the migration can be done.

It is always advised to have a full backup before upgrading Structr.

GraalVM Migration Guide

With the Structr 4.0 Release the required Java Runtime was changed to GraalVM and the JavaScript scripting engine underwent a large rewrite. This will give Structr 4.0 multiple advantages compared to the prior versions like full support of the latest ECMAscript standarts or better overall performance of the System. However those changes make a couple a manual migrations necessary which should be quite simple to make in a deployment export.

Installing GraalVM

Currently Structr depends on GraalVM 22.1.0. It can be installed on linux-based distributions using the following commands.

wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.1.0/graalvm-ce-java11-linux-amd64-22.1.0.tar.gz
tar xvzf graalvm-ce-java11-linux-amd64-22.1.0.tar.gz
sudo mv graalvm-ce-java11-22.1.0 /usr/lib/jvm
sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/graalvm-ce-java11-22.1.0/bin/java 2210
sudo update-alternatives --auto java

Scripting Migration

All predicates in advanced find scripting expressions need to be prefixed with $.predicate.

Example:

Before: $.find('File', 'size', $.range(null, 100), $.page(1, 10));
Now: $.find('File', 'size', $.predicate.range(null, 100), $.predicate.page(1, 10));

The easiest way to achieve this is to export the application using the deployment export and searching all files for occurrences of the advanced find predicate list:

$.and
$.or
$.not
$.equals
$.contains
$.empty
$.range
$.within_distance
$.sort
$.page

Only occurrences which are used in advanced find need to be prefixed. Some of those predicates also exist as regular functions ($.sort(), $.empty(), …) or keywords ($.page).

Migration Example
$.find('User', $.sort('createdDate'));

Needs to be migrated to:

$.find('User', $.predicate.sort('createdDate'));
No Migration Needed Example
$.sort($.find('User'), 'createdDate');

Here the $.sort() function is used to sort the nodes fetched via regular find and is not used within the find() function itself. Nothing has to be changed here!

Resource Access Grants Migration Guide

Resource Access Grants have been made more flexible in Structr 4.0 Release. Rights management now also applies to grant nodes. That means that users have to have read access to the grant object to be able to “use” it. Assigning visibility flags (visibleToPublicUsers, visibleToAuthenticatedUsers) or ACL read rights via users/groups is necessary for this. The other configuration of access flags for HTTP Verbs remain unchanged.

When upgrading to a version of Structr >= 4.0 (with an existing application), there are two (similar) approaches we can take to configuring the resource access grants: Manual migration and semi-automatic migration using the deployment.

Manual configuration

If we simply install Structr 4+ over an existing installation, automatic migration can not be done. But manual migration is quite simple following the following steps.

  1. Log in as admin in the administration backend
  2. Navigate to the “Security” area
  3. Activate the “Resource Access Grants” tab
  4. Activate the checkbox “Show only used grants”
  5. Migrate the grants according to these rules:
    • If the grant has active flags set for “Public Users” then the visibleToPublicUsers flag will be set to true
    • If the grant has active flags set for “Authenticated Users” then the visibleToAuthenticatedUsers flag will be set to true.
    • If the grant now has both visibility flags set, split it into two grants with identical signature

If there are a lot of grants to configure it is advisable to active the switch “Show visibility flags in Resource Access Grants table” on the “UI Settings” tab on the dashboard.

Semi-automatic Migration via Deployment

Importing a deployment export (which has been created with pre 4.0 version of Structr) into a 4+ version of Structr will run an auto-migration using a simple heuristic. This works because the security/grants.json which was created by the older version of Structr does not contain the visibility and grantees properties. Some manual changes might be necessary afterwards, though.

The heuristic works basically like step 5 from manual configuration:

  • If the grant has active flags set for “Public Users” then the visibleToPublicUsers flag will be set to true
  • If the grant has active flags set for “Authenticated Users” then the visibleToAuthenticatedUsers flag will be set to true.

If both flags are set, an additional warning is issued that the grant should be split into two grants with identical signature. This is due to the fact that an object with visibleToPublicUsers = true is also visible to authenticated users.

Scripting Considerations

Comparing Dates

When comparing dates in scripting environments, it is advised to use the getTime() function to compare their timestamp long values. This prevents some issues with comparing GraalVM ProxyDate entities.

{
return $.me.createdDate.getTime() <= $.now.getTime();
}

Conditional Chaining

While conditional chaining is generally supported in our JavaScript environments, there are some niche cases that are not fully supported yet by the GraalVM. Specifically trying to apply conditional chaining on a ProxyObject that might have a function member and then trying to call said function leads to errors. The following example illustrates this issue.

{
const obj = {
method1: () => "works"
};

// Works
obj.method1?.();

// Works, call doesn't get executed
obj.method2?.();

const proxyObject = $.retrieve('passedObject');

// Does not work and throws unsupported message exception
proxyObject.myMethod?.();
}

REST Request Keywords

In 4.0, we have changed a number of REST request parameter names to prevent name collisions with properties, such as order, page etc. Starting with version 4.0, the following request parameters must be prefixed with an underscore (unless the legacy mode setting is enabled in structr.conf).

page, pageSize, sort, order, loose, locale, latlon, location, state, house, country, postalCode, city, street, distance, outputNestingDepth, debugLoggingEnabled, forceResultCount, disableSoftLimit, parallelizeJsonOutput, batchSize

must become

_page, _pageSize, _sort, _order, _loose, _locale, _latlon, _location, _state, _house, _country, _postalCode, _city, _street, _distance, _outputNestingDepth, _debugLoggingEnabled,_forceResultCount, _disableSoftLimit, _parallelizeJsonOutput, _batchSize

There is a legacy mode available via the setting application.legacy.requestparameters.enabled = true in structr.conf. This allows using the old syntax without the prefix but is discouraged for new projects.

Neo4j Upgrade

Structr 4.x still supports neo4j 3.5, so an upgrade is not strictly necessary. If an upgrade to a more recent neo4j version is desired, the neo4j changelog and migration guide should be consulted before upgrading. Some deprecated features have been removed in neo4j 4.x, so one should be on the lookout. One resource one should consult is: https://neo4j.com/docs/cypher-manual/current/deprecations-additions-removals-compatibility/#cypher-compatibility

A prominent example is that the cypher parameter syntax has changed:

The old parameter syntax {param} was deprecated in Neo4j 3.0 and removed entirely in Neo4j 4.0.
Using it will result in a syntax error. However, it is still possible to use it, with warnings,
if you prefix the query with CYPHER 3.5. See Cypher Compatibility for further information.

If the version upgrade is from below Neo4j v4, then it is possible that the default database is called “graph.db” instead of the newer “neo4j” in version 4 and up. Since v4.1 Structr supports selecting a database (if supported by the Neo4j installation it is connecting to). The default database it connects to is the “neo4j” database - if the migration happened from an older version, the correct database name needs to be configured via structr.conf file using the “xxxx.database.connection.databasename” setting as shown in the following example in line 4.

YOUR_CONFIGURED_DB_NAME.database.connection.url = bolt://localhost:7687
YOUR_CONFIGURED_DB_NAME.database.connection.name = YOUR_CONFIGURED_DB_NAME
YOUR_CONFIGURED_DB_NAME.database.connection.password = your_neo4j_password
YOUR_CONFIGURED_DB_NAME.database.connection.databasename = graph.db
YOUR_CONFIGURED_DB_NAME.database.driver = org.structr.bolt.BoltDatabaseService