Merge branch 'main' into mocha
This commit is contained in:
10
.eslintrc
10
.eslintrc
@@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"extends": "eslint:recommended",
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@sap/cds/recommended"
|
||||||
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
|
"es2022": true,
|
||||||
"node": true,
|
"node": true,
|
||||||
"es6": true,
|
|
||||||
"jest": true,
|
"jest": true,
|
||||||
"mocha": true
|
"mocha": true
|
||||||
},
|
},
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2020
|
|
||||||
},
|
|
||||||
"globals": {
|
"globals": {
|
||||||
"SELECT": true,
|
"SELECT": true,
|
||||||
"INSERT": true,
|
"INSERT": true,
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -18,3 +18,5 @@ reviews/msg-box
|
|||||||
reviews/db/test.db
|
reviews/db/test.db
|
||||||
|
|
||||||
*.openapi3.json
|
*.openapi3.json
|
||||||
|
*.sqlite
|
||||||
|
*.db
|
||||||
|
|||||||
12
.vscode/settings.json
vendored
12
.vscode/settings.json
vendored
@@ -14,5 +14,13 @@
|
|||||||
"**/odata-v4/okra/**"
|
"**/odata-v4/okra/**"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mochaExplorer.parallel": true
|
"mochaExplorer.parallel": true,
|
||||||
}
|
"eslint.validate": [
|
||||||
|
"cds",
|
||||||
|
"csn",
|
||||||
|
"csv",
|
||||||
|
"csv (semicolon)",
|
||||||
|
"tsv",
|
||||||
|
"tab"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -56,7 +56,7 @@ const books = Vue.createApp ({
|
|||||||
} catch (err) { books.user = { id: err.message } }
|
} catch (err) { books.user = { id: err.message } }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}).mount("#app")
|
}).mount('#app')
|
||||||
|
|
||||||
books.getUserInfo()
|
books.getUserInfo()
|
||||||
books.fetch() // initially fill list of books
|
books.fetch() // initially fill list of books
|
||||||
@@ -65,3 +65,25 @@ document.addEventListener('keydown', (event) => {
|
|||||||
// hide user info on request
|
// hide user info on request
|
||||||
if (event.key === 'u') books.user = undefined
|
if (event.key === 'u') books.user = undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
|
axios.interceptors.request.use(csrfToken)
|
||||||
|
function csrfToken (request) {
|
||||||
|
if (request.method === 'head' || request.method === 'get') return request
|
||||||
|
if ('csrfToken' in document) {
|
||||||
|
request.headers['x-csrf-token'] = document.csrfToken
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
return fetchToken().then(token => {
|
||||||
|
document.csrfToken = token
|
||||||
|
request.headers['x-csrf-token'] = document.csrfToken
|
||||||
|
return request
|
||||||
|
}).catch(_ => {
|
||||||
|
document.csrfToken = null // set mark to not try again
|
||||||
|
return request
|
||||||
|
})
|
||||||
|
|
||||||
|
function fetchToken() {
|
||||||
|
return axios.get('/', { headers: { 'x-csrf-token': 'fetch' } })
|
||||||
|
.then(res => res.headers['x-csrf-token'])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
const cds = require('@sap/cds')
|
const cds = require('@sap/cds/lib')
|
||||||
|
|
||||||
module.exports = cds.service.impl (function(){
|
module.exports = class AdminService extends cds.ApplicationService { init(){
|
||||||
this.before ('NEW','Authors', genid)
|
this.before ('NEW','Authors', genid)
|
||||||
this.before ('NEW','Books', genid)
|
this.before ('NEW','Books', genid)
|
||||||
})
|
return super.init()
|
||||||
|
}}
|
||||||
|
|
||||||
/** Generate primary keys for target entity in request */
|
/** Generate primary keys for target entity in request */
|
||||||
async function genid (req) {
|
async function genid (req) {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ const cds = require('@sap/cds')
|
|||||||
|
|
||||||
class CatalogService extends cds.ApplicationService { init(){
|
class CatalogService extends cds.ApplicationService { init(){
|
||||||
|
|
||||||
const { Books } = this.entities ('sap.capire.bookshop')
|
const { Books } = cds.entities ('sap.capire.bookshop')
|
||||||
|
const { ListOfBooks } = this.entities
|
||||||
|
|
||||||
// Reduce stock of ordered books if available stock suffices
|
// Reduce stock of ordered books if available stock suffices
|
||||||
this.on ('submitOrder', async req => {
|
this.on ('submitOrder', async req => {
|
||||||
@@ -18,7 +19,7 @@ class CatalogService extends cds.ApplicationService { init(){
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Add some discount for overstocked books
|
// Add some discount for overstocked books
|
||||||
this.after ('READ','ListOfBooks', each => {
|
this.after ('READ', ListOfBooks, each => {
|
||||||
if (each.stock > 111) each.title += ` -- 11% discount!`
|
if (each.stock > 111) each.title += ` -- 11% discount!`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ service UserService {
|
|||||||
/**
|
/**
|
||||||
* The current user
|
* The current user
|
||||||
*/
|
*/
|
||||||
@odata.singleton entity me {
|
@odata.singleton entity me @cds.persistence.skip {
|
||||||
id : String; // user id
|
id : String; // user id
|
||||||
locale : String;
|
locale : String;
|
||||||
tenant : String;
|
tenant : String;
|
||||||
|
|||||||
@@ -19,4 +19,4 @@ module.exports = cds.server
|
|||||||
|
|
||||||
// For didactic reasons in capire
|
// For didactic reasons in capire
|
||||||
const { ReviewsService, OrdersService } = cds.requires
|
const { ReviewsService, OrdersService } = cds.requires
|
||||||
if (!ReviewsService.credentials && !OrdersService.credentials) cds.requires.messaging = false
|
if (!ReviewsService?.credentials && !OrdersService?.credentials) cds.requires.messaging = false
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
* Exposes data + entity metadata
|
* Exposes data + entity metadata
|
||||||
*/
|
*/
|
||||||
@requires:'authenticated-user'
|
@requires:'authenticated-user'
|
||||||
service DataService @( path:'-data' ) {
|
@odata service DataService @( path:'-data' ) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata like name and columns/elements
|
* Metadata like name and columns/elements
|
||||||
*/
|
*/
|
||||||
entity Entities {
|
entity Entities @cds.persistence.skip {
|
||||||
key name : String;
|
key name : String;
|
||||||
columns: Composition of many {
|
columns: Composition of many {
|
||||||
name : String;
|
name : String;
|
||||||
@@ -19,7 +19,7 @@ service DataService @( path:'-data' ) {
|
|||||||
/**
|
/**
|
||||||
* The actual data, organized by column name
|
* The actual data, organized by column name
|
||||||
*/
|
*/
|
||||||
entity Data {
|
entity Data @cds.persistence.skip {
|
||||||
record : array of {
|
record : array of {
|
||||||
column : String;
|
column : String;
|
||||||
data : String;
|
data : String;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ annotate CatalogService.Books with @(UI : {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Books Object Page
|
// Books List Page
|
||||||
//
|
//
|
||||||
annotate CatalogService.Books with @(UI : {
|
annotate CatalogService.Books with @(UI : {
|
||||||
SelectionFields : [
|
SelectionFields : [
|
||||||
|
|||||||
@@ -10,40 +10,31 @@ using { sap.common } from '@capire/common';
|
|||||||
// Books Lists
|
// Books Lists
|
||||||
//
|
//
|
||||||
annotate my.Books with @(
|
annotate my.Books with @(
|
||||||
Common.SemanticKey : [ID],
|
Common.SemanticKey : [ID],
|
||||||
UI : {
|
UI : {
|
||||||
Identification : [{Value : title}],
|
Identification : [{ Value: title }],
|
||||||
SelectionFields : [
|
SelectionFields : [
|
||||||
ID,
|
ID,
|
||||||
author_ID,
|
author_ID,
|
||||||
price,
|
price,
|
||||||
currency_code
|
currency_code
|
||||||
],
|
],
|
||||||
LineItem : [
|
LineItem : [
|
||||||
{
|
{ Value: ID, Label: '{i18n>Title}' },
|
||||||
Value : ID,
|
{ Value: author.ID, Label: '{i18n>Author}' },
|
||||||
Label : '{i18n>Title}'
|
{ Value: genre.name },
|
||||||
},
|
{ Value: stock },
|
||||||
{
|
{ Value: price },
|
||||||
Value : author.ID,
|
{ Value: currency.symbol, Label: ' ' },
|
||||||
Label : '{i18n>Author}'
|
]
|
||||||
},
|
}
|
||||||
{Value : genre.name},
|
|
||||||
{Value : stock},
|
|
||||||
{Value : price},
|
|
||||||
{
|
|
||||||
Value : currency.symbol,
|
|
||||||
Label : ' '
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
ID @Common: {
|
ID @Common: {
|
||||||
SemanticObject : 'Books',
|
SemanticObject : 'Books',
|
||||||
Text: title,
|
Text: title,
|
||||||
TextArrangement : #TextOnly
|
TextArrangement : #TextOnly
|
||||||
};
|
};
|
||||||
author @ValueList.entity : 'Authors';
|
author @ValueList.entity : 'Authors';
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -51,10 +42,10 @@ annotate my.Books with @(
|
|||||||
// Books Details
|
// Books Details
|
||||||
//
|
//
|
||||||
annotate my.Books with @(UI : {HeaderInfo : {
|
annotate my.Books with @(UI : {HeaderInfo : {
|
||||||
TypeName : '{i18n>Book}',
|
TypeName : '{i18n>Book}',
|
||||||
TypeNamePlural : '{i18n>Books}',
|
TypeNamePlural : '{i18n>Books}',
|
||||||
Title : {Value : title},
|
Title : { Value: title },
|
||||||
Description : {Value : author.name}
|
Description : { Value: author.name }
|
||||||
}, });
|
}, });
|
||||||
|
|
||||||
|
|
||||||
@@ -63,19 +54,13 @@ annotate my.Books with @(UI : {HeaderInfo : {
|
|||||||
// Books Elements
|
// Books Elements
|
||||||
//
|
//
|
||||||
annotate my.Books with {
|
annotate my.Books with {
|
||||||
ID @title : '{i18n>ID}';
|
ID @title: '{i18n>ID}';
|
||||||
title @title : '{i18n>Title}';
|
title @title: '{i18n>Title}';
|
||||||
genre @title : '{i18n>Genre}' @Common : {
|
genre @title: '{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly };
|
||||||
Text : genre.name,
|
author @title: '{i18n>Author}' @Common: { Text: author.name, TextArrangement: #TextOnly };
|
||||||
TextArrangement : #TextOnly
|
price @title: '{i18n>Price}' @Measures.ISOCurrency : currency_code;
|
||||||
};
|
stock @title: '{i18n>Stock}';
|
||||||
author @title : '{i18n>Author}' @Common : {
|
descr @UI.MultiLineText;
|
||||||
Text : author.name,
|
|
||||||
TextArrangement : #TextOnly
|
|
||||||
};
|
|
||||||
price @title : '{i18n>Price}' @Measures.ISOCurrency : currency_code;
|
|
||||||
stock @title : '{i18n>Stock}';
|
|
||||||
descr @UI.MultiLineText;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -83,17 +68,17 @@ annotate my.Books with {
|
|||||||
// Genres List
|
// Genres List
|
||||||
//
|
//
|
||||||
annotate my.Genres with @(
|
annotate my.Genres with @(
|
||||||
Common.SemanticKey : [name],
|
Common.SemanticKey : [name],
|
||||||
UI : {
|
UI : {
|
||||||
SelectionFields : [name],
|
SelectionFields : [name],
|
||||||
LineItem : [
|
LineItem : [
|
||||||
{Value : name},
|
{ Value: name },
|
||||||
{
|
{
|
||||||
Value : parent.name,
|
Value : parent.name,
|
||||||
Label : 'Main Genre'
|
Label: 'Main Genre'
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -101,18 +86,18 @@ annotate my.Genres with @(
|
|||||||
// Genre Details
|
// Genre Details
|
||||||
//
|
//
|
||||||
annotate my.Genres with @(UI : {
|
annotate my.Genres with @(UI : {
|
||||||
Identification : [{Value : name}],
|
Identification : [{ Value: name}],
|
||||||
HeaderInfo : {
|
HeaderInfo : {
|
||||||
TypeName : '{i18n>Genre}',
|
TypeName : '{i18n>Genre}',
|
||||||
TypeNamePlural : '{i18n>Genres}',
|
TypeNamePlural : '{i18n>Genres}',
|
||||||
Title : {Value : name},
|
Title : { Value: name },
|
||||||
Description : {Value : ID}
|
Description : { Value: ID }
|
||||||
},
|
},
|
||||||
Facets : [{
|
Facets : [{
|
||||||
$Type : 'UI.ReferenceFacet',
|
$Type : 'UI.ReferenceFacet',
|
||||||
Label : '{i18n>SubGenres}',
|
Label : '{i18n>SubGenres}',
|
||||||
Target : 'children/@UI.LineItem'
|
Target : 'children/@UI.LineItem'
|
||||||
}, ],
|
}, ],
|
||||||
});
|
});
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -120,8 +105,8 @@ annotate my.Genres with @(UI : {
|
|||||||
// Genres Elements
|
// Genres Elements
|
||||||
//
|
//
|
||||||
annotate my.Genres with {
|
annotate my.Genres with {
|
||||||
ID @title : '{i18n>ID}';
|
ID @title: '{i18n>ID}';
|
||||||
name @title : '{i18n>Genre}';
|
name @title: '{i18n>Genre}';
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -129,24 +114,24 @@ annotate my.Genres with {
|
|||||||
// Authors List
|
// Authors List
|
||||||
//
|
//
|
||||||
annotate my.Authors with @(
|
annotate my.Authors with @(
|
||||||
Common.SemanticKey : [ID],
|
Common.SemanticKey : [ID],
|
||||||
UI : {
|
UI : {
|
||||||
Identification : [{Value : name}],
|
Identification : [{ Value: name}],
|
||||||
SelectionFields : [name],
|
SelectionFields : [name],
|
||||||
LineItem : [
|
LineItem : [
|
||||||
{Value : ID},
|
{ Value: ID },
|
||||||
{Value : dateOfBirth},
|
{ Value: dateOfBirth },
|
||||||
{Value : dateOfDeath},
|
{ Value: dateOfDeath },
|
||||||
{Value : placeOfBirth},
|
{ Value: placeOfBirth },
|
||||||
{Value : placeOfDeath},
|
{ Value: placeOfDeath },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
ID @Common: {
|
ID @Common: {
|
||||||
SemanticObject : 'Authors',
|
SemanticObject : 'Authors',
|
||||||
Text: name,
|
Text: name,
|
||||||
TextArrangement : #TextOnly,
|
TextArrangement : #TextOnly,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -154,16 +139,16 @@ annotate my.Authors with @(
|
|||||||
// Author Details
|
// Author Details
|
||||||
//
|
//
|
||||||
annotate my.Authors with @(UI : {
|
annotate my.Authors with @(UI : {
|
||||||
HeaderInfo : {
|
HeaderInfo : {
|
||||||
TypeName : '{i18n>Author}',
|
TypeName : '{i18n>Author}',
|
||||||
TypeNamePlural : '{i18n>Authors}',
|
TypeNamePlural : '{i18n>Authors}',
|
||||||
Title : {Value : name},
|
Title : { Value: name },
|
||||||
Description : {Value : dateOfBirth}
|
Description : { Value: dateOfBirth }
|
||||||
},
|
},
|
||||||
Facets : [{
|
Facets : [{
|
||||||
$Type : 'UI.ReferenceFacet',
|
$Type : 'UI.ReferenceFacet',
|
||||||
Target : 'books/@UI.LineItem'
|
Target : 'books/@UI.LineItem'
|
||||||
}, ],
|
}, ],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -172,12 +157,12 @@ annotate my.Authors with @(UI : {
|
|||||||
// Authors Elements
|
// Authors Elements
|
||||||
//
|
//
|
||||||
annotate my.Authors with {
|
annotate my.Authors with {
|
||||||
ID @title : '{i18n>ID}';
|
ID @title: '{i18n>ID}';
|
||||||
name @title : '{i18n>Name}';
|
name @title: '{i18n>Name}';
|
||||||
dateOfBirth @title : '{i18n>DateOfBirth}';
|
dateOfBirth @title: '{i18n>DateOfBirth}';
|
||||||
dateOfDeath @title : '{i18n>DateOfDeath}';
|
dateOfDeath @title: '{i18n>DateOfDeath}';
|
||||||
placeOfBirth @title : '{i18n>PlaceOfBirth}';
|
placeOfBirth @title: '{i18n>PlaceOfBirth}';
|
||||||
placeOfDeath @title : '{i18n>PlaceOfDeath}';
|
placeOfDeath @title: '{i18n>PlaceOfDeath}';
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -185,18 +170,18 @@ annotate my.Authors with {
|
|||||||
// Languages List
|
// Languages List
|
||||||
//
|
//
|
||||||
annotate common.Languages with @(
|
annotate common.Languages with @(
|
||||||
Common.SemanticKey : [code],
|
Common.SemanticKey : [code],
|
||||||
Identification : [{Value : code}],
|
Identification : [{ Value: code}],
|
||||||
UI : {
|
UI : {
|
||||||
SelectionFields : [
|
SelectionFields : [
|
||||||
name,
|
name,
|
||||||
descr
|
descr
|
||||||
],
|
],
|
||||||
LineItem : [
|
LineItem : [
|
||||||
{Value : code},
|
{ Value: code },
|
||||||
{Value : name},
|
{ Value: name },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -204,22 +189,22 @@ annotate common.Languages with @(
|
|||||||
// Language Details
|
// Language Details
|
||||||
//
|
//
|
||||||
annotate common.Languages with @(UI : {
|
annotate common.Languages with @(UI : {
|
||||||
HeaderInfo : {
|
HeaderInfo : {
|
||||||
TypeName : '{i18n>Language}',
|
TypeName : '{i18n>Language}',
|
||||||
TypeNamePlural : '{i18n>Languages}',
|
TypeNamePlural : '{i18n>Languages}',
|
||||||
Title : {Value : name},
|
Title : { Value: name },
|
||||||
Description : {Value : descr}
|
Description : { Value: descr }
|
||||||
},
|
},
|
||||||
Facets : [{
|
Facets : [{
|
||||||
$Type : 'UI.ReferenceFacet',
|
$Type : 'UI.ReferenceFacet',
|
||||||
Label : '{i18n>Details}',
|
Label : '{i18n>Details}',
|
||||||
Target : '@UI.FieldGroup#Details'
|
Target : '@UI.FieldGroup#Details'
|
||||||
}, ],
|
}, ],
|
||||||
FieldGroup #Details : {Data : [
|
FieldGroup #Details : {Data : [
|
||||||
{Value : code},
|
{ Value: code },
|
||||||
{Value : name},
|
{ Value: name },
|
||||||
{Value : descr}
|
{ Value: descr }
|
||||||
]},
|
]},
|
||||||
});
|
});
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -227,19 +212,19 @@ annotate common.Languages with @(UI : {
|
|||||||
// Currencies List
|
// Currencies List
|
||||||
//
|
//
|
||||||
annotate common.Currencies with @(
|
annotate common.Currencies with @(
|
||||||
Common.SemanticKey : [code],
|
Common.SemanticKey : [code],
|
||||||
Identification : [{Value : code}],
|
Identification : [{ Value: code}],
|
||||||
UI : {
|
UI : {
|
||||||
SelectionFields : [
|
SelectionFields : [
|
||||||
name,
|
name,
|
||||||
descr
|
descr
|
||||||
],
|
],
|
||||||
LineItem : [
|
LineItem : [
|
||||||
{Value : descr},
|
{ Value: descr },
|
||||||
{Value : symbol},
|
{ Value: symbol },
|
||||||
{Value : code},
|
{ Value: code },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -247,35 +232,35 @@ annotate common.Currencies with @(
|
|||||||
// Currency Details
|
// Currency Details
|
||||||
//
|
//
|
||||||
annotate common.Currencies with @(UI : {
|
annotate common.Currencies with @(UI : {
|
||||||
HeaderInfo : {
|
HeaderInfo : {
|
||||||
TypeName : '{i18n>Currency}',
|
TypeName : '{i18n>Currency}',
|
||||||
TypeNamePlural : '{i18n>Currencies}',
|
TypeNamePlural : '{i18n>Currencies}',
|
||||||
Title : {Value : descr},
|
Title : { Value: descr },
|
||||||
Description : {Value : code}
|
Description : { Value: code }
|
||||||
|
},
|
||||||
|
Facets : [
|
||||||
|
{
|
||||||
|
$Type : 'UI.ReferenceFacet',
|
||||||
|
Label : '{i18n>Details}',
|
||||||
|
Target : '@UI.FieldGroup#Details'
|
||||||
},
|
},
|
||||||
Facets : [
|
{
|
||||||
{
|
$Type : 'UI.ReferenceFacet',
|
||||||
$Type : 'UI.ReferenceFacet',
|
Label : '{i18n>Extended}',
|
||||||
Label : '{i18n>Details}',
|
Target : '@UI.FieldGroup#Extended'
|
||||||
Target : '@UI.FieldGroup#Details'
|
},
|
||||||
},
|
],
|
||||||
{
|
FieldGroup #Details : {Data : [
|
||||||
$Type : 'UI.ReferenceFacet',
|
{ Value: name },
|
||||||
Label : '{i18n>Extended}',
|
{ Value: symbol },
|
||||||
Target : '@UI.FieldGroup#Extended'
|
{ Value: code },
|
||||||
},
|
{ Value: descr }
|
||||||
],
|
]},
|
||||||
FieldGroup #Details : {Data : [
|
FieldGroup #Extended : {Data : [
|
||||||
{Value : name},
|
{ Value: numcode },
|
||||||
{Value : symbol},
|
{ Value: minor },
|
||||||
{Value : code},
|
{ Value: exponent }
|
||||||
{Value : descr}
|
]},
|
||||||
]},
|
|
||||||
FieldGroup #Extended : {Data : [
|
|
||||||
{Value : numcode},
|
|
||||||
{Value : minor},
|
|
||||||
{Value : exponent}
|
|
||||||
]},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -283,7 +268,7 @@ annotate common.Currencies with @(UI : {
|
|||||||
// Currencies Elements
|
// Currencies Elements
|
||||||
//
|
//
|
||||||
annotate common.Currencies with {
|
annotate common.Currencies with {
|
||||||
numcode @title : '{i18n>NumCode}';
|
numcode @title: '{i18n>NumCode}';
|
||||||
minor @title : '{i18n>MinorUnit}';
|
minor @title: '{i18n>MinorUnit}';
|
||||||
exponent @title : '{i18n>Exponent}';
|
exponent @title: '{i18n>Exponent}';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,10 +43,10 @@
|
|||||||
"[production]": {
|
"[production]": {
|
||||||
"model": "db/hana"
|
"model": "db/hana"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"hana": {
|
|
||||||
"deploy-format": "hdbtable"
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"hana": {
|
||||||
|
"deploy-format": "hdbtable"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,23 +12,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "*",
|
"@types/jest": "*",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"ts-jest": "^27.0.2",
|
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.3.5"
|
||||||
},
|
},
|
||||||
"jest": {
|
|
||||||
"testEnvironment": "node",
|
|
||||||
"preset": "ts-jest",
|
|
||||||
"globals": {
|
|
||||||
"ts-jest": {
|
|
||||||
"diagnostics": {
|
|
||||||
"_comment": "see https://githubmemory.com/repo/kulshekhar/ts-jest/issues/2722",
|
|
||||||
"ignoreCodes": [
|
|
||||||
151001
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "eslint:recommended",
|
"extends": "eslint:recommended",
|
||||||
"env": {
|
"env": {
|
||||||
|
|||||||
13
hello/test/hello-world-test.js
Normal file
13
hello/test/hello-world-test.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const cds = require ('@sap/cds')
|
||||||
|
describe('Hello world!', () => {
|
||||||
|
|
||||||
|
beforeAll (()=> process.env.CDS_TYPESCRIPT = true)
|
||||||
|
afterAll (()=> delete process.env.CDS_TYPESCRIPT)
|
||||||
|
const {GET} = cds.test.in(__dirname,'../srv').run('serve', 'world.cds')
|
||||||
|
|
||||||
|
it('should say hello with class impl', async () => {
|
||||||
|
const {data} = await GET`/say/hello(to='world')`
|
||||||
|
expect(data.value).toMatch(/Hello world.*typescript.*/i)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
process.env.CDS_TYPESCRIPT = 'true';
|
|
||||||
import * as cds from '@sap/cds';
|
|
||||||
|
|
||||||
//@ts-ignore
|
|
||||||
const {GET} = cds.test.in(__dirname,'../srv').run('serve', 'world.cds');
|
|
||||||
|
|
||||||
describe('Hello world!', () => {
|
|
||||||
afterAll(() => { delete process.env.CDS_TYPESCRIPT; });
|
|
||||||
|
|
||||||
it('should say hello with class impl from a typescript file', async () => {
|
|
||||||
const {data} = await GET`/say/hello(to='world')`
|
|
||||||
expect(data.value).toMatch(/Hello world.*typescript.*/i)
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
@@ -16,11 +16,12 @@ using { OrdersService } from '../srv/orders-service';
|
|||||||
@odata.draft.enabled
|
@odata.draft.enabled
|
||||||
annotate OrdersService.Orders with @(
|
annotate OrdersService.Orders with @(
|
||||||
UI: {
|
UI: {
|
||||||
SelectionFields: [ createdAt, createdBy ],
|
SelectionFields: [ createdBy ],
|
||||||
LineItem: [
|
LineItem: [
|
||||||
{Value: OrderNo, Label:'OrderNo'},
|
{Value: OrderNo, Label:'OrderNo'},
|
||||||
{Value: buyer, Label:'Customer'},
|
{Value: buyer, Label:'Customer'},
|
||||||
{Value: createdAt, Label:'Date'}
|
{Value: currency.symbol, Label:'Currency'},
|
||||||
|
{Value: createdAt, Label:'Date'},
|
||||||
],
|
],
|
||||||
HeaderInfo: {
|
HeaderInfo: {
|
||||||
TypeName: 'Order', TypeNamePlural: 'Orders',
|
TypeName: 'Order', TypeNamePlural: 'Orders',
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
applications: {
|
applications: {
|
||||||
"manage-orders": {
|
"manage-orders": {
|
||||||
title: "Manage Orders",
|
title: "Manage Orders",
|
||||||
description: "... testing FE v42",
|
description: "CAP Sample App",
|
||||||
additionalInformation: "SAPUI5.Component=orders",
|
additionalInformation: "SAPUI5.Component=orders",
|
||||||
applicationType : "URL",
|
applicationType : "URL",
|
||||||
url: "/orders/webapp",
|
url: "/orders/webapp",
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
"sap.app": {
|
"sap.app": {
|
||||||
"id": "orders",
|
"id": "orders",
|
||||||
"type": "application",
|
"type": "application",
|
||||||
"title": "Order Books",
|
"title": "Order Management",
|
||||||
"description": "Sample Application",
|
"description": "CAP Sample Application",
|
||||||
"i18n": "i18n/i18n.properties",
|
"i18n": "i18n/i18n.properties",
|
||||||
"dataSources": {
|
"dataSources": {
|
||||||
"OrdersService": {
|
"OrdersService": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using { Currency, User, managed, cuid } from '@sap/cds/common';
|
|||||||
namespace sap.capire.orders;
|
namespace sap.capire.orders;
|
||||||
|
|
||||||
entity Orders : cuid, managed {
|
entity Orders : cuid, managed {
|
||||||
OrderNo : String @title:'Order Number'; //> readable key
|
OrderNo : String(22) @title:'Order Number'; //> readable key
|
||||||
Items : Composition of many {
|
Items : Composition of many {
|
||||||
key ID : UUID;
|
key ID : UUID;
|
||||||
product : Association to Products;
|
product : Association to Products;
|
||||||
|
|||||||
7029
package-lock.json
generated
7029
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -8,7 +8,15 @@
|
|||||||
"@sap/cds": ">=5.5.3"
|
"@sap/cds": ">=5.5.3"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"./*/"
|
"./bookshop",
|
||||||
|
"./bookstore",
|
||||||
|
"./common",
|
||||||
|
"./data-viewer",
|
||||||
|
"./fiori",
|
||||||
|
"./hello",
|
||||||
|
"./media",
|
||||||
|
"./orders",
|
||||||
|
"./reviews"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios": "^0",
|
"axios": "^0",
|
||||||
|
|||||||
@@ -411,18 +411,67 @@ describe('cds.ql → cqn', () => {
|
|||||||
]
|
]
|
||||||
}})
|
}})
|
||||||
|
|
||||||
expect (
|
const ql_with_groups_fix = !!cds.ql.Query.prototype.flat
|
||||||
SELECT.from(Foo).where({x:1,or:{y:2}})
|
if (ql_with_groups_fix) {
|
||||||
).to.eql (
|
|
||||||
CQL`SELECT from Foo where x=1 or y=2`
|
expect (
|
||||||
).to.eql ({ SELECT: {
|
SELECT.from(Foo).where({x:1}).or({y:2}).and({z:3})
|
||||||
from: {ref:['Foo']},
|
).to.eql ({ SELECT: {
|
||||||
where: [
|
from: {ref:['Foo']},
|
||||||
{ref:['x']}, '=', {val:1},
|
where: [
|
||||||
'or',
|
{ref:['x']}, '=', {val:1},
|
||||||
{ref:['y']}, '=', {val:2}
|
'or',
|
||||||
]
|
{ref:['y']}, '=', {val:2},
|
||||||
}})
|
'and',
|
||||||
|
{ref:['z']}, '=', {val:3},
|
||||||
|
]
|
||||||
|
}})
|
||||||
|
|
||||||
|
expect (
|
||||||
|
SELECT.from(Foo).where({x:1,or:{y:2}}).and({z:3})
|
||||||
|
).to.eql ({ SELECT: {
|
||||||
|
from: {ref:['Foo']},
|
||||||
|
where: [
|
||||||
|
{xpr:[
|
||||||
|
{ref:['x']}, '=', {val:1},
|
||||||
|
'or',
|
||||||
|
{ref:['y']}, '=', {val:2},
|
||||||
|
]},
|
||||||
|
'and',
|
||||||
|
{ref:['z']}, '=', {val:3},
|
||||||
|
]
|
||||||
|
}})
|
||||||
|
|
||||||
|
expect (
|
||||||
|
SELECT.from(Foo).where({a:1}).or({x:1,or:{y:2}}).and({z:3})
|
||||||
|
).to.eql ({ SELECT: {
|
||||||
|
from: {ref:['Foo']},
|
||||||
|
where: [
|
||||||
|
{ref:['a']}, '=', {val:1},
|
||||||
|
'or',
|
||||||
|
{xpr:[
|
||||||
|
{ref:['x']}, '=', {val:1},
|
||||||
|
'or',
|
||||||
|
{ref:['y']}, '=', {val:2},
|
||||||
|
]},
|
||||||
|
'and',
|
||||||
|
{ref:['z']}, '=', {val:3},
|
||||||
|
]
|
||||||
|
}})
|
||||||
|
|
||||||
|
expect (
|
||||||
|
{ SELECT: SELECT.from(Foo).where({x:1,or:{y:2}}).SELECT }
|
||||||
|
).to.eql ({ SELECT: {
|
||||||
|
from: {ref:['Foo']},
|
||||||
|
where: [
|
||||||
|
{ref:['x']}, '=', {val:1},
|
||||||
|
'or',
|
||||||
|
{ref:['y']}, '=', {val:2},
|
||||||
|
]
|
||||||
|
}})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
expect (
|
expect (
|
||||||
SELECT.from(Foo).where({x:1,and:{y:2}}).or({z:3})
|
SELECT.from(Foo).where({x:1,and:{y:2}}).or({z:3})
|
||||||
|
|||||||
@@ -5,21 +5,10 @@ describe('cap/samples - Localized Data', () => {
|
|||||||
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
|
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
|
||||||
|
|
||||||
it('serves localized $metadata documents', async () => {
|
it('serves localized $metadata documents', async () => {
|
||||||
const { data } = await GET`/browse/$metadata?sap-language=de`
|
const { data } = await GET(`/browse/$metadata?sap-language=de`, { headers: { 'accept-language': 'de' }})
|
||||||
expect(data).to.contain('<Annotation Term="Common.Label" String="Währung"/>')
|
expect(data).to.contain('<Annotation Term="Common.Label" String="Währung"/>')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('supports sap-language param', async () => {
|
|
||||||
const { data } = await GET(`/browse/Books?$select=title,author` + '&sap-language=de')
|
|
||||||
expect(data.value).to.containSubset([
|
|
||||||
{ title: 'Sturmhöhe', author: 'Emily Brontë' },
|
|
||||||
{ title: 'Jane Eyre', author: 'Charlotte Brontë' },
|
|
||||||
{ title: 'The Raven', author: 'Edgar Allen Poe' },
|
|
||||||
{ title: 'Eleonora', author: 'Edgar Allen Poe' },
|
|
||||||
{ title: 'Catweazle', author: 'Richard Carpenter' },
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('supports accept-language header', async () => {
|
it('supports accept-language header', async () => {
|
||||||
const { data } = await GET(`/browse/Books?$select=title,author`, {
|
const { data } = await GET(`/browse/Books?$select=title,author`, {
|
||||||
headers: { 'Accept-Language': 'de' },
|
headers: { 'Accept-Language': 'de' },
|
||||||
|
|||||||
Reference in New Issue
Block a user