10.0 Changelog
This page lists all the changes introduced in 10.0 releases. See also the release notes and the migration guide.
10.0.0 (2026-10-23)
Feature
-
Add the workflow automation engine
Details
Added a lightweight, declarative workflow engine bound to models: a workflow starts from one trigger (
create,update,timer,manual, or amessageanother workflow sends) and walks a graph of nodes until anend, covering both automation and human-approval flows. Workflows are authored as XML undersrc/main/resources/workflows/, validated against an XSD plus a graph-wide validator, then loaded intoMetaWorkflowrecords the same way views and actions are. Everything runs after the triggering transaction commits, so a workflow failure never rolls back the user’s save.Node types:
script(inline JavaScript against the record),agent(ask an AI agent, optionally checking its answer against a declared shape),email(send an email template),splitandjoin(branch onone,someorallflows and wait forall,anyor a quorum of them),call(run another workflow, once or per record),subflow(a region of the same workflow, once or per record),task(a human approval with an assignee, a user, or a role or group pool),wait(park on a timer, a condition, a message or a signal),send(announce a message or broadcast a signal),boundary(a deadline, condition, message, signal or error attached to another node) andend.An
agentoremailnode runs in the background: the run stops there, and the node runs on a worker of its own once everything before it has committed, so a slow model or mail server neither holds up nor undoes the save. A slowscriptopts in withasync="true", and any of the three opts out withasync="false". The record’s form shows such a run as pending and reloads the record once the node commits, so an edit made meanwhile does not collide with the newer version.Pending tasks surface in a My Approvals inbox and on the record form’s toolbar, both permission-checked per task. A workflow’s
idis its stable identity, and each run is pinned to the revision it started on, so parked runs and tasks survive undeploy and redeploy.Execution history is kept at three levels, set by
workflow.trace:offrecords failures,runsadds a row per run, andnodesadds a row per node visited. Parks are recorded at every level. A run form lists the steps it took, and a Node Metrics view groups them by node across runs.Workflows can also be built visually in a Studio designer that saves and deploys through the same validation, and authored from chat through a Studio tool exposed to the AI assistant.
-
Resolve the client IP address from a single trusted header
Details
Behind the proxy filter,
HttpServletRequest#getRemoteAddr()now returns the client IP address resolved from a single header: the left-most address of the header configured with the newapplication.client-ip-headersetting, or ofX-Forwarded-Forby default. The address is validated and normalized, ie any port suffix is stripped and IPv6 addresses are canonicalized, whereas the left-mostX-Forwarded-Forvalue was previously used as it is.If that header is missing, or doesn’t provide a valid IP address, the address of the direct connection is used. No other header is tried, such as
X-Real-IP, as the reverse proxy may not overwrite it, so it may have been forged by the client.# header providing the client IP address, set by the reverse proxy application.client-ip-header = X-Real-IP
The configured header is trusted as it is: the reverse proxy must overwrite it, and the application must only be reachable through the proxy. With nginx, using the default
X-Forwarded-Forheader:proxy_set_header X-Forwarded-For $remote_addr;
Don’t use
$proxy_add_x_forwarded_for: it keeps theX-Forwarded-Forvalue sent by the client, whose left-most address would then be used as the client address. -
Add typed JSON extract functions for longs, dates and times
Details
PostgreSQL gains the JPQL functions
json_extract_long,json_extract_date,json_extract_datetimeandjson_extract_time, so a long, date, date-time or time held in a JSON field can be read with its type and passed to date functions. A JSON path names them with::long,::date,::datetimeand::time;::longpreviously read as a 32-bit integer. -
Add advanced search support to record selector popup
Details
The record selector popup (opened from many-to-one/many-to-many/one-to-many select actions) now shows an advanced search bar in its header, letting users build custom search criteria over the selector’s grid columns instead of relying only on the plain free-text search.
-
Give notification popups more time and a way to keep them open
Details
The notification popups shown in the bottom corner disappeared after three seconds, which left no time to read a long message, let alone copy it, and no way to keep one on screen.
Popups now last five seconds, and stay for as long as the pointer rests on them or the keyboard focus is inside them. The five seconds start over once the pointer leaves.
Every popup also carries a close button, so it can be dismissed on the spot instead of waiting for it to fade.
-
Add
ReportConfigurerto customize report generationDetails
ReportGeneratornow delegates application specific behavior to an injectableReportConfigurer:-
validate(report, params): called once the design is opened, throw to abort the generation -
configure(task, options, params): called before the task runs (timezone, render options…)
Override
ReportConfigurerImplinstead of extendingReportGenerator, so that report generation fixes and improvements still apply to your application:bind(ReportConfigurerImpl.class).to(MyReportConfigurer.class); -
-
Add FieldSelector component for browsing model fields in grid view customizer
Details
A new FieldSelector component allows users to browse and select fields from a model, including traversal through relational fields (many-to-one, one-to-one), producing a dotted field path (e.g. partner.fullName).
The component is integrated into the grid view customizer toolbar as an Add New action, allowing arbitrary dotted field paths to be added as columns without requiring them to be declared in the view XML.
Key behaviors:
-
Fields are listed alphabetically and filterable via a search input.
-
Clicking a relational field drills into its target model; breadcrumb tags in the trigger reflect the current navigation path.
-
Keyboard navigation: ArrowUp/ArrowDown to move, ArrowRight to drill into a relational field, Enter to select, Backspace to navigate back.
-
Fields already fetched for a model are cached and not re-fetched on revisit.
-
-
Throttle refresh of API key
lastUsedAtDetails
API key authentication is stateless, so it runs on every single request. The
lastUsedAtof the API key was written on each of them, turning a read-only workload into oneUPDATEplus one commit per request, and invalidating the cache regions ofUserTokenjust as often.lastUsedAtis now refreshed at most once every 5 minutes. That interval is wide enough to tell active API keys from unused ones, which is what the field is for, while keeping clients making frequent calls from writing to the database on each of them. Once the interval has elapsed, a lease of 30 seconds makes sure a single request refreshes the timestamp, instead of every request that was already in flight.Both mechanisms prevent row-level lock contention and API response delay when dozens of concurrent requests use the same token.
-
Support Java text blocks in translatable messages
Details
Java text blocks can now be used for translatable messages, either with
I18n.get()or with the/*$$(*/ ... /*)*/marker, and are extracted by thei18ntask:I18n.get( """ <ul> <li>First line <li>Second line </ul>\ """); -
Log user logins
Details
Successful authentication attempts are now recorded as AuthenticationEvent. Users can see the history of their own attempts from the user and preferences forms. Admins can see the attempts of every user from the new "Connections" menu. Direct clients such as API key and basic authentication are excluded, and external providers only report their successful logins. Events older than 180 days are deleted daily by the new "authentication.events.purge" scheduled job. The retention period can be changed with the "auth.events.retention" setting.
-
Add the AI assistant
Details
A conversational AI assistant, available from every screen of an application. It answers questions, looks up and changes records through tools, draws on indexed knowledge, and can build the application with Studio. All of it is configuration under the AI menu: providers (OpenAI, Anthropic, Google, Mistral, Ollama or a custom endpoint) and their models, which can be synced from the provider; agents, each with a prompt, a model, a cheaper utility model for background work, and the tools, skills, knowledge bases and sub-agents it may use; tools of several kinds — system, client, script, HTTP, action, and those of external MCP servers; skills loaded on demand; and knowledge bases indexing documents and records. Everything seeds from files under a module’s
ai/directory and travels between databases in config bundles.Answers stream into the chat panel, keep running when the browser disconnects, and show each tool call as a card; a tool can ask for the user’s approval first. An answer can carry a chart, table, KPI or other data widget, drawn from a query run as the user. A conversation is pinned to the record it is about (
/subjectshows or changes it), can be asked about form fields picked on screen, and is summed up with/recap. Procedures let an agent change a record the way a user would, through the form’s own steps, and the Agent Builder creates and edits agents in conversation.A conversation can be shared: members follow it, post in it, or, with "Can ask", bring in the agent, whose answers show who asked. In assistant mode a plain message runs the agent; in discussion mode it goes to the members, and
/discussswitches between them. A shared conversation marked as a space keeps its name and is offered again when sharing with the same people. The assistant button opens what is waiting: the one unread conversation, or the history filtered to the unread ones.The assistant never goes beyond the user’s own permissions, field rules included, on every path it reads or writes through: data tools, queries, procedures, the open form, mentions, recaps and record knowledge. A field that comes along unasked and that the user may not read is withheld and listed under
$hidden, and a call with no user is refused. Within that, each agent lists the models its job needs and the operations allowed there (["* read", "Order read update"]in an agent file); an agent with none reaches no data, and a consulted agent is held to every agent before it. Data policies (AI › Configuration) say what the AI may never read — fields of a model, a whole model, or the records a condition covers — for every call when global, else for the agents, models or providers they list. A refusal names the operation and the model, and whether the user or the assistant lacks the access. A Studio tool needs the user’s own right to Studio’s resources.A conversation keeps the fields whose values entered it, and every model it is later sent to must be allowed to receive them: a turn on a model that may not is refused before any call, with the offer of a new conversation; a consultation of such an agent is skipped; a background call (title, summary, recap) falls back to the turn’s model, or is skipped. A record knowledge source declares the fields its template reads, and indexing refuses those a policy keeps from the embedding model.
Usage policies cap the monthly chat tokens and the request rate of a role, group or user. Every model call, background tasks and embeddings included, is in the usage log with the fields it carried, or as REFUSED when it was not sent.
The AI tables of the former Enterprise Edition changed: permissions on models replace an agent’s
allowed_models, usage policies are named and list the roles, groups and users they apply to, and cross-session memories are removed. See the 10.0 migration guide for the SQL script. -
Support a JWKS file for the OpenID Connect private key JWT client authentication
Details
The signing key of the private key JWT client authentication config can be read from a JWKS file:
auth.provider.keycloak.client-authentication-method = private_key_jwt auth.provider.keycloak.private-key-jwt-client-authn-method-config.jwks.path = /path/to/jwks.json auth.provider.keycloak.private-key-jwt-client-authn-method-config.jwks.kid = my-key
The
validity,key-clock-skewanduse-expirationsettings are also supported.Configuring a private key with
private-key.path,private-key.algorithm,jws-algorithmandkey-idis no longer supported. See the migration guide for more details. -
Add current tenant in logs
Details
When multi-tenancy is enabled, log lines didn’t tell which tenant they belong to, making it hard to troubleshoot an issue for a given tenant in the shared log file.
A new
%tenantpattern converter outputs the tenant identifier, with an optional fallback used when no tenant is set on the thread, e.g.%tenant{-}. The raw identifier is also available as%X{tenant}for customlogback.xmlconfigurations.When multi-tenancy is enabled, the
LOG_TENANT_PATTERNvariable is set to[%tenant{-}] +`. Default console and file patterns include it as `${LOG_TENANT_PATTERN:-}+right before the thread name. When disabled, it is empty and the log format is unchanged:... INFO 973172 --- [acme] [http-nio-8080-exec-1] com.axelor... : ... ... INFO 973172 --- [-] [main] com.axelor... : ...
-
Allow restricting API keys to IP addresses or CIDR ranges
Details
API keys can now be restricted to a comma-separated list of IP addresses or CIDR ranges (IPv4 and IPv6), from the API key form. A restricted API key used from any other address is rejected. An API key without any restriction keeps working from any address.
A new
allowed_ipscolumn is added to theauth_user_tokentable:ALTER TABLE auth_user_token ADD allowed_ips varchar(2048);
Change
-
Upgrade backend dependencies
Details
Here is the list of backend dependencies upgraded:
-
Gradle from 8.14.5 to 9.8.2
-
HikariCP from 7.0.2 to 7.1.0
-
Logback from 1.5.38 to 1.6.5
-
Caffeine from 3.2.4 to 3.3.1
-
Guava from 33.5.0 to 33.7.2
-
CommonsIO from 2.21.0 to 2.22.0
-
Apache Tika from 3.2.3 to 3.3.2
-
Snakeyaml from 2.6 to 2.7
-
Birt from 4.23.0 to 4.25.0
-
ASM from 9.9.1 to 9.10.1
-
Woodstox from 7.1.1 to 7.2.2
-
Junit from 6.0.3 to 6.1.3
-
Jline from 4.1.3 to 4.4.8
-
Redisson from 4.7.0 to 4.8.0
-
Byte Buddy from 1.18.14 to 1.18.15
-
Tomcat from 10.1.60 to 10.1.61
-
PostgreSQL JDBC Driver from 42.7.13 to 42.7.14
-
-
Improve JavaScript scripting performance and result durability
Details
JavaScript action-scripts and expressions now run faster and return durable results. The wall-clock script timeout is enforced with a lightweight interrupt watchdog instead of attaching a per-evaluation execution listener (which GraalVM documents as expensive and able to deoptimize compiled code); the configured timeout and the resulting
ScriptTimeoutExceptionare unchanged. Script results that are arrays or objects are now returned as plain, durable JavaList/Mapvalues instead of live context-bound proxies, so they remain valid after the script helper is closed. A cached compiled-function API (execute(Source, scope)with named sources) was added for callers that run the same script repeatedly. JavaScriptMapandSetresults are returned asMap/Listof their entries, integers too large forlongasBigIntegerinstead of a lossydouble, and aDateas theInstantit stands for instead of aLocalDate, which lost its time and took the day of the JVM’s time zone. -
Resolve bare type names to host types in JavaScript actions
Details
In JavaScript action-scripts and expressions, a bare type name (a model, repository, or built-in helper like
LocalDateTime,BigDecimal,Arrays) now resolves to a GraalVM host type instead of ajava.lang.Classinstance. Static members and constructors now work everywhere, e.g.LocalDateTime.now(),Arrays.asList(1, 2, 3),new BigDecimal('1.50')andnew Contact().The type stays usable as a
Classargument (__repo__(Contact),__bean__(MyService),context.asType(Contact)) and withinstanceof(record instanceof Contact), because GraalVM unwraps a host type to itsjava.lang.Classwhen passed to aClassparameter. Only the JavaScript engine is affected; Groovy scripts are unchanged.Breaking changes:
java.lang.Classinstance methods are no longer callable on a bare type name. Reach theClassthrough.class, or use the equivalent operator:-
Contact.getName()→Contact.class.getName() -
Contact.getSimpleName()→Contact.class.getSimpleName() -
Contact.isInstance(record)→record instanceof Contact -
Contact.isAssignableFrom(other)→Contact.class.isAssignableFrom(other)
Finding them: this is rare and only affects entity (model) class names. Any such call now fails loudly at run time with
TypeError: ... Unknown identifier: <method>, naming the exact call. To audit ahead of time, search where scripts live — action XML files, and action definitions stored in the database (meta_action.xml) — for a capitalized receiver calling one of these methods:grep -rEn '\b[A-Z][A-Za-z0-9_]*\.(getName|getSimpleName|getCanonicalName|isInstance|isAssignableFrom)\(' .
Most matches are calls on ordinary objects; only the ones whose receiver is an entity/model class name need changing. Fix them by hand using the mappings above — there will be few.
-
-
JSON fields performance improvements
Details
Performance improved by caching results from MetaStore#findJsonFields(). This improves custom model data import performance and other JSON fields operations.
JsonProperty.getTitle()now returns untranslated title, to realign consistency withProperty.getTitle().Contextno longer applies user permission/visibility filtering when checking whether a JSON field exists, to realign consistency with how regular (non-JSON) fields are resolved.JSON field and JSON model changes are reflected by
MetaStoreonly once the transaction completes. Reading them back within the same transaction may return the previously cached state. -
List many-to-many values inline in advanced export and import
Details
Advanced export writes a many-to-many field, or a collection custom field, as one column of its owner, the related records' keys separated by
|(a|or\inside a key is escaped with\). It does so in both standard and flat export, so a flat export can now include the main model’s many-to-many fields. Link sheets such asOrderLine.taxesare no longer exported unless the Link sheets option is chosen, which a flat export does not offer.On import, such a cell is the record’s complete set: values left out are unlinked, a blank cell clears the collection, and a file without the column leaves it unchanged. Link sheets exported by earlier versions still import, and only add links.
Import now orders the sheets of a file by the records they reference, and sets references to records created later in the same sheet, or around a cycle across sheets, in a second pass. A reference that still names no record is reported in the result’s Errors sheet, and its record is kept without it. A cycle made only of required references can’t be imported and is refused before anything is written.
Many-to-many changes are now written through the ORM rather than directly to the join table, so auditing, workflows and cached collections see them.
-
One edition under the Axelor Commercial License
Details
Axelor Open Platform no longer has separate Community and Enterprise editions. All features of the Enterprise Edition are now part of the platform: AI assistants and agents, Studio, global search, real-time collaboration on views, data import/export, and additional SSO settings (provider order, default provider, local and central logout).
The whole platform is now distributed under the Axelor Commercial License, instead of the AGPL v3 for the former Community Edition. See the
LICENSEfile.The
axelor-enterprise-editionmodule and theaxelor-core-enterpriseandaxelor-web-enterpriseartifacts are no longer published: their content is now inaxelor-coreandaxelor-web. Theaxelor.platform.eeGradle property is no longer used.Former
Enterprise*classes are now merged into their base classes or renamed, such asEnterpriseAvailableAppSettingsintoAvailableAppSettingsandEnterpriseAppFeaturesintoAppFeatures.See the 10.0 migration guide for the upgrade steps.
-
Improve
@DynamicUpdatesupportDetails
Currently,
@DynamicUpdateis added on entity having a computed persisted field. The annotation prevents using cachedPreparedStatementbut it generates a new SQL string specifically for that transaction, compiles it, and executes it.We now apply this annotation only when the cost of generating the SQL is outweighed by the savings in database I/O. Cases are :
-
large tables (more than 100 XML-declared column-backed fields): prevent massive, unnecessary data payloads
-
tables with large objects (binary/large fields): ensure heavy binary/large data is not re-transmitted unless it is actually changed
Also, allow enabling/disabling the
@DynamicUpdatesupport on demand per entity through thedynamicUpdateattribute for more control.JSON fields like
attrsdo not trigger@DynamicUpdateautomatically because their runtime size is unknown and most entities leaveattrsempty. Users should setdynamicUpdate="true"explicitly on entities with large JSON payloads. -
-
Promote command line application to GA
Details
The command line application is no longer in beta.
The distribution archives are now built as part of the standard Gradle lifecycle:
build(andassemble) produce both the ZIP and TAR archives inbuild/distributions/. Previously, they were only generated whendistZip,distTarorassembleDistwere explicitly requested.The extracted distribution can still be generated in
build/install/with theinstallDisttask.
Remove
-
Remove support of
context.appLogoandcontext.appIconmethods withoutmodeparameterDetails
The methods referenced by the
context.appLogoandcontext.appIconsettings must now accept a singleString modeparameter (lightordark). Methods without parameter, deprecated since 7.3, are no longer called and the default application logo/icon is used instead. -
Remove deprecated
sequentialattribute on entitiesDetails
The
sequentialattribute on<entity>definitions, deprecated and ignored since 8.0, is removed from the domain models schema. Each entity uses its own dedicated sequence. Remove the attribute from domain XML files. -
Remove
@DynamicInsertsupportDetails
@DynamicInserton entities provides almost zero benefit. In fact, it often decreases performance because it preventsPreparedStatementcaching. The support is removed. No changes are expected. -
Remove deprecated
TenantAwareandAuditableRunnerDetails
com.axelor.db.tenants.TenantAwareandcom.axelor.auth.AuditableRunner, deprecated since 8.0, are removed. UseContextAwareto build aContextAwareRunnableorContextAwareCallableinstead. See the migration guide for details.
Fix
-
Fix the tenant host changed for the rest of the request after a synchronous context-aware run
Details
Running a
ContextAwaretask on the calling request thread now restores the tenant host taken from the request. It was replaced by the first host configured for the tenant, or cleared when the tenant has no configured hosts, which affected tenant base URLs built later in the same request. -
Fix report data sets failing with "Connection is closed"
Details
Reports run outside a transaction lent the request’s database connection to BIRT. Any entity query made while rendering, such as
ImageToolloading a company logo, released that connection to the pool. The data sets queried afterwards failed withConnection is closed, and the report was generated empty or incomplete.Outside a transaction, reports now run on a dedicated database connection. Within a transaction, they still use the transaction’s connection and see its uncommitted changes.
-
Fix collaboration user names when User namecolumn is redefined
Details
When the namecolumn of the User entity was redefined (for example to
fullName), the collaboration avatars shown when several users open the same record had no initials, and their tooltip showed "undefined" instead of the user’s name. They now show the user’s initials and name again. -
Fix global search indexer using the batch size as its thread count
Details
The mass indexer read its thread count from
global.search.indexer.batch-size, soglobal.search.indexer.threadshad no effect. Setting a batch size of 500 started 500 threads. The indexer now readsglobal.search.indexer.threads. -
Fix adding a custom model record from an editable grid
Details
In the editable grid of a custom model, adding a new row and saving it failed with "jsonModel - must not be null". New rows now get the custom model set, as new records created from the form view already did.
-
Fix save data on m2m grid reordering
Details
Unlike o2m grid, when move operation is performed on m2m grid then it should directly saved changed order of records to server.
-
Fix editable grid wiping custom field values
Details
In an editable grid showing custom fields, whether on a custom model or on a regular entity, leaving a row right after clicking into it could save the record with all its custom field values emptied, and with its name emptied on a custom model. The grid now keeps the row’s values, and no longer saves a row that was left unchanged.
-
Fix saving a new user
Details
Creating a user failed on save, because the MFA configuration of the user was looked up before the user was persisted. New users are now saved without error.
-
Fix error after multi-factor authentication when login was opened directly
Details
When the login page was opened directly instead of through a redirect from another page, completing multi-factor authentication ended on an "Unexpected Application Error" (
Cannot read properties of undefined (reading 'pathname')). The user is now redirected to the home page. -
Fix websocket handshake refused behind a proxy forwarding the wss scheme
Details
Without
application.base-url, the origin check of the websocket handshake refused every connection behind a reverse proxy that forwards the upgrade request withX-Forwarded-Proto: wss(orws), such as Traefik: the base url was built with thewss://scheme while the browser sent anhttps://origin. Thewsandwssschemes are now compared ashttpandhttps. -
Fix add item in movable top-level editable grid
-
Fix the user leaking to the next request after a synchronous context-aware run
Details
Running a
ContextAwaretask on the calling request thread no longer leaves the session user in the current-user thread-local. The next request served by the same pooled thread could otherwise run as the previous caller. -
Fix realtime features stopping after the websocket connection drops
Details
After the websocket connection dropped, for example on a network outage or a server restart, it was not reconnected until a message had to be sent, and the reconnected socket did not subscribe to its channels again. The connection is now re-established automatically with an increasing delay, and its channels and rooms are restored when it reopens.
A failing open or close callback no longer stops the other channels from being restored, and a failing message listener no longer keeps the message from the other listeners.
-
Fix setting the OpenID Connect private key JWT client authentication config
Details
Since pac4j 6.4.0, the
private-key-jwt-client-authn-method-configsetting of an OpenID Connect provider couldn’t be set anymore.The return type from
OidcConfiguration.privateKeyJwtClientAuthnMethodConfigwas changed fromPrivateKeyJWTClientAuthnMethodConfigtoIPrivateKeyJwtClientAuthnMethodConfig. -
Fix JavaScript action-scripts failing with "The Context is already closed"
Details
A JavaScript
<action-script>that set a value from an object or array literal, such as$response.setValue('customer', {id: 1}), failed withThe Context is already closed. The literal was kept as a proxy bound to the script’s context, which was closed before the response was sent to the client. Response values are now copied into plain Java values before the context is closed. -
Do not apply field domain when searching JSON relational references on import
Details
When importing data (CSV/XML), searching for an existing custom (JSON) record to reference is now scoped only by
self.jsonModel, no longer by the JSON field’sdomain. The field domain should be a UI selectability constraint only. -
Fix values lost when chaining actions on a saved record
Details
On a saved record, when several actions were chained together (for example in an action-group or an onChange), the values set by some of them were lost if one of the actions returned the whole record and another set only a few fields. The form kept its stale values and was not marked as modified. All values returned by the chained actions are now applied, the later action winning for a given field.
Security
-
Validate temporary file names
-
Serve report files by token
Details
The
ws/files/reportendpoint, which accepted a temporary file name with thelinkparameter, has been removed. Report files are now served by thews/files/data-exportendpoint with atokenparameter, provided by the newActionResponse.setFilemethod and byaction-report. A report token is only valid for the user who created it, and expires after 5 minutes.ActionResponse.setFilereplaces theActionResponse.setExportFilemethods. It takes aResponseFile, which the client opens in a new tab for preview if created withResponseFile.previewand supported by the browser, or always downloads if created withResponseFile.download:// Before response.setView(ActionView.define(title).add("html", fileLink).map()); response.setExportFile(path, fileName); // After: open the report in a new tab response.setFile(ResponseFile.preview(path, fileName).title(title)); // After: download the file response.setFile(ResponseFile.download(path, fileName)); -
Serve data export and import result files by token
Details
The
ws/data/downloadandws/rest/{model}/export/{name}endpoints, which accepted a temporary file name, have been removed. Thews/data/export,ws/data/importandws/rest/{model}/exportendpoints now return atokento download the file with thews/files/data-exportendpoint.Pending export files (
ws/data/export,ws/data/import,ws/rest/{model}/export,ActionResponse.setFileandaction-export) are now only accessible by the user who created them.