$ file -bi myfile.pdf
application/octet-stream; charset=binary
[CAP] Set credentials in package.json via .env
These three entries in a .env file
cds.requires.ECEmploymentInformation.[development].credentials.authentication=BasicAuthentication
cds.requires.ECEmploymentInformation.[development].credentials.username=myUsername
cds.requires.ECEmploymentInformation.[development].credentials.password=myPassword
are equal to line 12, 13, and 14 in this package.json snippet:
{
"cds": {
"ECEmploymentInformation": {
"kind": "odata-v2",
"model": "srv/external/ECEmploymentInformation",
"credentials": {
"[production]": {
"destination": "sfsf"
},
"[development]": {
"url": "https://apisalesdemo2.successfactors.eu/odata/v2",
"authentication": "BasicAuthentication",
"username": "myUsername",
"password": "myPassword",
}
}
}
}
}
[Linux Mint] Sharkoon PureWriter Keyboard not recognized after suspend
In January 2020 I bought a Sharkoon PureWriter Keyboard and since then I had the problem that the keyboard got not recognized after my PC (which runs on Linux Mint) was coming back from suspend mode. Back then I couldn’t find a solution and was just hoping that a newer kernel release will fix this problem in the future. But it did not. So today I was searching again and stumbled again across this post, but now I noticed the new answer from April this year. And it finally solved it!
First check with usbreset for the device name and then create the script under the following path:
sudo nano /lib/systemd/system-sleep/reset-keyboard
#!/bin/sh
case $1 in
post)
usbreset "USB-HID Keyboard"
;;
esac
And as a last step we make it executable:
sudo chmod +x /lib/systemd/system-sleep/reset-keyboard
[Postman] Visualize base64 image
If you have a service which returns a payload like the following (including a base64 encoded jpeg) you can display it directly in postman.
{
"photo": "/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsK\r\nCwsND..............",
"photoId": "192",
"mimeType": "image/jpeg"
}
This can be done with a few lines of code. In Postman navigate to the “Tests” tab:

and insert the following lines:
//output to postman console
console.log("PhotoId: " + pm.response.json()["photoId"]);
console.log("Base64: " + pm.response.json()["photo"]);
//output in visualize tab
let template = `<img src='{{img}}'/>`;
pm.visualizer.set(template, {
img: `data:image/jpeg;base64,${pm.response.json()["photo"]}`
});
In the “Visualize” tab you should now find your image

[SAPUI5] Call function of another controller using EventBus
https://sapui5.hana.ondemand.com/#/api/sap.ui.core.EventBus%23overview
In the receiving controller you need to subscribe your eventId and function you want to call from the second controller:
// Attaches an event handler to the event with the given identifier on the given event channel
this.getOwnerComponent().getEventBus().subscribe("Default", "myEventId", () => {
this._myFunctionIWantToCall();
});
The sending controller has to publish the event to trigger the function call:
// Fires an event using the specified settings and notifies all attached event handlers.
this.getOwnerComponent().getEventBus().publish("Default", "myEventId", {});
[SAPUI5] Get and set properties of a binded model and submit changes
Get:
const oModel = this.getView().getModel()
const sPath = this.getView().getBindingContext().sPath
const sID = oModel.getProperty(sPath+"/ID")
Set:
const newID = "12345"
oModel.setProperty(sPath+"/ID", newID)
When using the set property function, you can now submit your changes this way:
// First check if there are any changes
if (!oModel.hasPendingChanges()) {
MessageToast.show("Nothing to do!")
return
}
// Now submit your changes
oModel.submitChanges({
success: () => MessageToast.show("Success!"),
error: (err) => alert(err)
})
This way is much more comfortable, than using oModel.update().
[CAP] SAPUI5 Filter using FilterOperator.Contains with oData V2
In my SAPUI5 Freesstyle frontend I created a search field above a list. In the searchfield handler I’m creating a filter with the provided query.
const sQuery = oEvent.getParameter("query");
new Filter("firstName", FilterOperator.Contains, sQuery);
Afterwards I’m binding the filter to my list to trigger the binding refresh. But when debugging the backend handler I noticed the following…
In my CAP on Read handler, the filter gets converted into a V4 compatible filter expression:
oData V4: $filter=contains(firstName,'Max')
As I’m forwarding the request to an external V2 oData API (SuccessFactors) this would not work, as for V2 the following filter syntax is needed:
oData V2: $filter=substringof('Max',firstName) eq true
As I could not find any solution to this problem, I manually passed my filter as custom property to my CAP Service and did a manual select.
Adding the custom property in the frontend in my searchfield handler:
onSearch: function (oEvent) {
if (oEvent.getParameters().refreshButtonPressed) {
this.onRefresh();
return;
}
let oBindingInfo = this._oList.getBindingInfo("items");
if (!oBindingInfo.parameters) oBindingInfo.parameters = {};
if (!oBindingInfo.parameters.custom) oBindingInfo.parameters.custom = {};
if (oEvent.getParameter("query")) {
oBindingInfo.parameters.custom.filter = "%" + oEvent.getParameter("query") + "%";
} else {
oBindingInfo.parameters.custom.filter = undefined
}
this._oList.bindItems(oBindingInfo);
}
My CAP handler with the filter handling:
const { Object } = srv.entities
const SF_Srv = await cds.connect.to('SF')
srv.on('READ', Object, async req => {
if (!req._queryOptions.filter) {
// share request context with the external service
return SF_Srv.tx(req).run(req.query);
} else {
//if filter provided, build manually a select statement using a where condition
let input = req._queryOptions.filter;
const tx = SF_Srv.transaction(req);
return await tx.run(
SELECT
.from(Object)
.where`firstName like ${input} or lastName like ${input}`)
}
})
As alternative you could also add the where condition directly to the query object:
const { Object } = srv.entities
const SF_Srv = await cds.connect.to('SF')
srv.on('READ', Object, async req => {
if (req._query.filter) {
//if filter provided, build manually a select statement using a where condition
let { query } = req
let input = req._queryOptions.filter
if (!query.SELECT.where) query.SELECT["where"] = []
query.SELECT.where.push(
{ ref: ['firstName'] }, 'like', { val: input }, "or",
{ ref: ['lastName'] }, 'like', { val: input }, "or",
{ ref: ['object'] }, 'like', { val: input })
}
// share request context with the external service
return SF_Srv.tx(req).run(req.query)
})
[PDF.js] Prevent loading default pdf
This can be done by appending ?file= to the src path, like it is mentionend here.
<!-- "?file=" prevents loading the default document -->
<iframe id="pdf-js-viewer" src="/pdf/web/viewer.html?file=" title="webviewer" frameborder="0" width="100%" height="700" allowfullscreen="" webkitallowfullscreen=""/>
Or by setting the variable defaultUrl to blank using the onload event.
document.addEventListener("webviewerloaded", async () => {
let pdfViewerIFrame = document.getElementById("pdf-js-viewer")
//https://github.com/mozilla/pdf.js/blob/master/web/app_options.js
pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("defaultUrl", "") //prevent loading default pdf
})
[CAP] Using a Tree in SAPUI5 Freestyle app
The following Links helped me implementing the tree functionality:
https://sapui5.hana.ondemand.com/#/entity/sap.m.Tree/sample/sap.m.sample.TreeOData
https://answers.sap.com/questions/13192367/sap-cds-how-to-add-hierarchy-annotations-saphierar.html
Define the data model in data-model.cds
entity Node {
key NodeID : Integer;
HierarchyLevel : Integer;
ParentNodeID : Integer;
Description : String;
drillState : String;
}
Create testdata in my.test-Node.csv
NodeID;HierarchyLevel;ParentNodeID;drillState;Description
1;0;null;"expanded";"1"
2;0;null;"expanded";"2"
3;0;null;"expanded";"3"
4;1;1;"leaf";"1.1"
5;1;1;"expanded";"1.2"
6;2;5;"leaf";"1.2.1"
7;2;5;"leaf";"1.2.2"
and deploy the testdata to your local sql db
cds deploy --to sqlite:db/test.db
Service Definition in test-service.cds
using my.test as db from '../db/data-model';
service testService {
entity Nodes as projection on db.Node;
}
add the Tree controll to your Fiori UI view Tree.view.xml
<Tree
id="Tree"
items="{path: '/Nodes',
parameters : {
countMode: 'Inline',
numberOfExpandedLevels: 3,
treeAnnotationProperties: {
hierarchyLevelFor : 'HierarchyLevel',
hierarchyNodeFor : 'NodeID',
hierarchyParentNodeFor : 'ParentNodeID',
hierarchyDrillStateFor : 'drillState'
}
}
}">
<StandardTreeItem title="{Description}"/>
</Tree>
The output should be similar to this:

