Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Localized & draft & ID_texts #677

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions advanced/fiori.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,97 @@ Adding the annotation `@fiori.draft.enabled` won't work if the corresponding `_t

If you're editing data in multiple languages, the _General_ tab in the example above is reserved for the default language (often "en"). Any change to other languages has to be done in the _Translations_ tab, where a corresponding language can be chosen from a drop-down menu as illustrated above. This also applies if you use the URL parameter `sap-language` on the draft page.

#### Background

> TODO: find a good place for this section, maybe make it internal
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


When we initially built draft support, the assumption was that most apps would not maintain
translations for texts in the data (like product names and descriptions) via the UI,
but do the translation externally and then bring in the translated texts via mass data upload.
Thus the default is that if an entity is draft enabled via `@odata.draft.enabled`,
the corresponding `.texts` entity is _not_ part of the draft tree, although it is a composition child of
the main entity.

If such a `.texts` entity should take part in the draft game, this must be explicitly switched
stewsk marked this conversation as resolved.
Show resolved Hide resolved
on by also adding the annotation `@fiori.draft.enabled` to the main entity.
This annotation does however not only include the `.texts` entity into the draft tree, but it also
changes the key of the `.texts`entity.
At the time draft support was implemented in CAP, Fiori required (TODO is this true?)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is true

stewsk marked this conversation as resolved.
Show resolved Hide resolved
that a draft enabled entity must have a single key element of type UUID.
As a `.texts` entity by construction has at least two key elements (the `locale` plus the key elements
of the main entity), we remove the `key` property from all these elements and add a technical key element
`ID_texts` of type UUID.

Example:
```cds
service S {
@odata.draft.enabled
@fiori.draft.enabled
entity Books {
key ID : Integer;
title : localized String;
}
}
```
Generated tables:
```sql
CREATE TABLE S_Books (
ID INTEGER NOT NULL,
title NVARCHAR(255),
PRIMARY KEY(ID)
);
CREATE TABLE S_Books_drafts (
ID INTEGER NOT NULL,
title NVARCHAR(255) NULL,
-- ... special draft fields
PRIMARY KEY(ID)
);
CREATE TABLE S_Books_texts (
ID_texts NVARCHAR(36) NOT NULL, -- <-- new key field
locale NVARCHAR(14),
ID INTEGER,
title NVARCHAR(255),
PRIMARY KEY(ID_texts),
CONSTRAINT S_Books_texts_locale UNIQUE (locale, ID)
);
CREATE TABLE S_Books_texts_drafts (
ID_texts NVARCHAR(36) NOT NULL,
locale NVARCHAR(14) NULL,
ID INTEGER NULL,
title NVARCHAR(255) NULL,
-- ... special draft fields
PRIMARY KEY(ID_texts)
);
```

This is the current state, but it has another problem. The `.texts` entity now has a key column
that the developer didn't define and doesn't need. So when providing initial data in a `csv` file,
this column usually isn't part of it.

Deployment (for PostgreSQL) has recently been improved to handle this. For each entry, the values of
the original key elements are used to generate a hash value which is then used as value for
`ID_texts`. It is important the the generated value is stable, so that upon redeploment the same values
stewsk marked this conversation as resolved.
Show resolved Hide resolved
for `ID_texts` are produced.

Today Fiori can also deal with composite keys with non-UUID types. For an entity with such a key,
Fiori cannot generate an initial value. So when creating a new entry, a pop-up is launched asking
the user to provide the key values.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather say that the backend doesn't allow key changes, hence Fiori Elements shows a popup to set the value once and doesn't allow to modify the value afterwards.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add that to the text / change the text accordingly

David-Kunz marked this conversation as resolved.
Show resolved Hide resolved

Is there a way to avoid this popup, but on the other hand keep the structure of the `.texts` entity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is possible via annotation to suppress the popup (don't ask me what it is). node backend generates values for all uuid key properties, regardless how many. so it might be possible to just prompt the user for the locale info...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's possible to suppress the pop up for non-UUID keys. It's something, the user cannot change, even in draft mode, hence the moment the user 'hops out' of the field, it would need to become immutable - Fiori elements can't do that.

and table unchanged?

The basic idea is to have in the CDS runtime and for the database a `.texts` entity and table with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so it would be the following?

CREATE TABLE S_Books_texts (
  locale NVARCHAR(14) NOT NULL,
  ID INTEGER NOT NULL,
  title NVARCHAR(255),
  PRIMARY KEY(ID, locale)
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the basic idea for what?

the original structure, but a `.texts.drafts` entity and table with `ID_texts` as key.
The entity in the OData API would also have the `ID_texts` key.

This only works, however, if we can guarantee that Fiori never sends a query based on `ID_texts`.
So, an ObjectPage for a text entry would not work.


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: when creating the new instance, we need to generate the id in the backend. at that point, however, we don't yet know the intended locale -> anything that tries to concatenate a fake id field on read (e.g., ID + '_' + locale as ID_texts) is doomed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anything that tries to concatenate a fake id field on read (e.g., ID + '_' + locale as ID_texts) is doomed.

Not during runtime, only when doing the CSV import, so it should be fine.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In CAP Java CSVs are also uploaded by the runtime (in case of in-memory DBs). We chould have a well-defined hashing algorithm, which is used accross the runtimes.





### Validating Drafts

You can add [custom handlers](../guides/providing-services#custom-logic) to add specific validations, as usual. In addition, for a draft, you can register handlers to the `PATCH` events to validate input per field, during the edit session, as follows.
Expand Down
Loading