diff --git a/bookshop/app/vue/app.js b/bookshop/app/vue/app.js index aa2d1721..0f923685 100644 --- a/bookshop/app/vue/app.js +++ b/bookshop/app/vue/app.js @@ -19,13 +19,13 @@ const books = Vue.createApp ({ search: ({target:{value:v}}) => books.fetch(v && '&$search='+v), async fetch (etc='') { - const {data} = await GET(`/ListOfBooks?$expand=genre($select=name),currency($select=symbol)${etc}`) + const {data} = await GET(`/Books?${etc}`) books.list = data.value }, async inspect (eve) { const book = books.book = books.list [eve.currentTarget.rowIndex-1] - const res = await GET(`/Books/${book.ID}?$select=descr,stock,image`) + const res = await GET(`/Book/${book.ID}?$select=descr,stock,image`) Object.assign (book, res.data) books.order = { quantity:1 } setTimeout (()=> $('form > input').focus(), 111) diff --git a/bookshop/app/vue/index.html b/bookshop/app/vue/index.html index 9795a65a..074eb754 100644 --- a/bookshop/app/vue/index.html +++ b/bookshop/app/vue/index.html @@ -45,11 +45,11 @@

Capire Books

{{ book.title }} {{ book.author }} - {{ book.genre.name }} + {{ book.genre }} {{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }} ({{ book.numberOfReviews }}) - {{ book.currency && book.currency.symbol }} {{ book.price }} + {{ book.currency }} {{ book.price }} diff --git a/bookshop/srv/admin-service.cds b/bookshop/srv/admin-service.cds index c5048e58..463773f9 100644 --- a/bookshop/srv/admin-service.cds +++ b/bookshop/srv/admin-service.cds @@ -1,5 +1,5 @@ using { sap.capire.bookshop as my } from '../db/schema'; service AdminService @(requires:'admin', path:'/admin') { entity Books as projection on my.Books; - entity Authors as projection on my.Authors; + // entity Authors as projection on my.Authors; } diff --git a/bookshop/srv/cat-service.cds b/bookshop/srv/cat-service.cds index 2441db25..6e991a64 100644 --- a/bookshop/srv/cat-service.cds +++ b/bookshop/srv/cat-service.cds @@ -2,15 +2,20 @@ using { sap.capire.bookshop as my } from '../db/schema'; service CatalogService @(path:'/browse') { /** For displaying lists of Books */ - @readonly entity ListOfBooks as projection on Books - excluding { descr }; + @readonly entity Books as projection on Book excluding { descr }; /** For display in details pages */ - @readonly entity Books as projection on my.Books { *, - author.name as author - } excluding { createdBy, modifiedBy }; + @readonly entity Book as projection on my.Books { *, + currency.name as currencyName, // flattened + currency.symbol as currency, // flattened + author.name as author, // flattened + genre.name as genre, // flattened + } excluding { + createdBy, modifiedBy, // as end users shouldn't see them + localized, texts, // as end users don't need them + }; @requires: 'authenticated-user' - action submitOrder ( book: Books:ID, quantity: Integer ) returns { stock: Integer }; - event OrderedBook : { book: Books:ID; quantity: Integer; buyer: String }; + action submitOrder ( book: Book:ID, quantity: Integer ) returns { stock: Integer }; + event OrderedBook : { book: Book:ID; quantity: Integer; buyer: String }; } diff --git a/bookshop/srv/cat-service.js b/bookshop/srv/cat-service.js index 40896f4b..1b8943d0 100644 --- a/bookshop/srv/cat-service.js +++ b/bookshop/srv/cat-service.js @@ -3,10 +3,10 @@ const cds = require('@sap/cds') class CatalogService extends cds.ApplicationService { init() { const { Books } = cds.entities('sap.capire.bookshop') - const { ListOfBooks } = this.entities + const { Books:Book } = this.entities // Add some discount for overstocked books - this.after('each', ListOfBooks, book => { + this.after('each', Book, book => { if (book.stock > 111) book.title += ` -- 11% discount!` }) diff --git a/bookshop/test/requests.http b/bookshop/test/requests.http index cbd8faff..7f7db483 100644 --- a/bookshop/test/requests.http +++ b/bookshop/test/requests.http @@ -1,5 +1,6 @@ @server = http://localhost:4004 @me = Authorization: Basic {{$processEnv USER}}: +@alice = Authorization: Basic alice: ### ------------------------------------------------------------------------ @@ -16,11 +17,11 @@ GET {{server}}/browse/$metadata ### ------------------------------------------------------------------------ # Browse Books as any user -GET {{server}}/browse/ListOfBooks? +GET {{server}}/admin/Books? # &$select=title,stock &$expand=genre # &sap-language=de -{{me}} +{{alice}} ### ------------------------------------------------------------------------ @@ -30,13 +31,13 @@ GET {{server}}/admin/Authors? # &$expand=books($select=title;$expand=currency) # &$filter=ID eq 101 # &sap-language=de -Authorization: Basic alice: +{{alice}} ### ------------------------------------------------------------------------ # Create Author POST {{server}}/admin/Authors Content-Type: application/json;IEEE754Compatible=true -Authorization: Basic alice: +{{alice}} { "ID": 112, @@ -49,7 +50,7 @@ Authorization: Basic alice: # Create book POST {{server}}/admin/Books Content-Type: application/json;IEEE754Compatible=true -Authorization: Basic alice: +{{alice}} { "ID": 2, @@ -67,7 +68,7 @@ Authorization: Basic alice: # Put image to books PUT {{server}}/admin/Books(2)/image Content-Type: image/png -Authorization: Basic alice: +{{alice}} data:image/png;base64,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 diff --git a/fiori/app/browse/fiori-service.cds b/fiori/app/browse/fiori-service.cds index 015e265b..99ea9afc 100644 --- a/fiori/app/browse/fiori-service.cds +++ b/fiori/app/browse/fiori-service.cds @@ -4,7 +4,7 @@ using CatalogService from '@capire/bookstore'; // // Books Object Page // -annotate CatalogService.Books with @(UI : { +annotate CatalogService.Book with @(UI : { HeaderInfo : { TypeName : '{i18n>Book}', TypeNamePlural : '{i18n>Books}', @@ -24,7 +24,7 @@ annotate CatalogService.Books with @(UI : { FieldGroup #Price : {Data : [ {Value : price}, { - Value : currency.symbol, + Value : currencyName, Label : '{i18n>Currency}' }, ]}, @@ -35,11 +35,11 @@ annotate CatalogService.Books with @(UI : { // // Books List Page // -annotate CatalogService.Books with @(UI : { +annotate CatalogService.Book with @(UI : { SelectionFields : [ ID, price, - currency_code + currencyName ], LineItem : [ { @@ -50,8 +50,10 @@ annotate CatalogService.Books with @(UI : { Value : author, Label : '{i18n>Author}' }, - {Value : genre.name}, + {Value : genre}, {Value : price}, - {Value : currency.symbol}, + {Value : currencyName}, ] -}, ); +}) { + currencyName @Common.Label : '{i18n>Currency}'; +}; diff --git a/fiori/app/common.cds b/fiori/app/common.cds index 3e5f748a..78abde14 100644 --- a/fiori/app/common.cds +++ b/fiori/app/common.cds @@ -18,7 +18,6 @@ annotate my.Books with @( ID, author_ID, price, - currency_code ], LineItem : [ { Value: ID, Label: '{i18n>Title}' }, @@ -26,7 +25,6 @@ annotate my.Books with @( { Value: genre.name }, { Value: stock }, { Value: price }, - { Value: currency.symbol }, ] } ) { @@ -63,7 +61,7 @@ annotate my.Books with { title @title: '{i18n>Title}'; genre @title: '{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly }; author @title: '{i18n>Author}' @Common: { Text: author.name, TextArrangement: #TextOnly }; - price @title: '{i18n>Price}' @Measures.ISOCurrency : currency_code; + price @title: '{i18n>Price}' @Measures.ISOCurrency : currency; stock @title: '{i18n>Stock}'; descr @title: '{i18n>Description}' @UI.MultiLineText; image @title: '{i18n>Image}'; diff --git a/test/localized-data/services.test.js b/test/localized-data/services.test.js index b4bc7a20..3ed01ee5 100644 --- a/test/localized-data/services.test.js +++ b/test/localized-data/services.test.js @@ -27,15 +27,15 @@ describe('cap/samples - Localized Data', () => { }) it('supports queries with $expand', async () => { - const { data } = await GET(`/browse/Books?&$select=title,author&$expand=currency`, { + const { data } = await GET(`/browse/Book?&$select=title,author,currencyName`, { headers: { 'Accept-Language': 'de' }, }) expect(data.value).to.containSubset([ - { title: 'Sturmhöhe', author: 'Emily Brontë', currency: { name: 'Pfund' } }, - { title: 'Jane Eyre', author: 'Charlotte Brontë', currency: { name: 'Pfund' } }, - { title: 'The Raven', author: 'Edgar Allen Poe', currency: { name: 'US-Dollar' } }, - { title: 'Eleonora', author: 'Edgar Allen Poe', currency: { name: 'US-Dollar' } }, - { title: 'Catweazle', author: 'Richard Carpenter', currency: { name: 'Yen' } }, + { title: 'Sturmhöhe', author: 'Emily Brontë', currencyName: 'Pfund' }, + { title: 'Jane Eyre', author: 'Charlotte Brontë', currencyName: 'Pfund' }, + { title: 'The Raven', author: 'Edgar Allen Poe', currencyName: 'US-Dollar' }, + { title: 'Eleonora', author: 'Edgar Allen Poe', currencyName: 'US-Dollar' }, + { title: 'Catweazle', author: 'Richard Carpenter', currencyName: 'Yen' }, ]) }) diff --git a/test/odata.test.js b/test/odata.test.js index 449ff60d..89b539e3 100644 --- a/test/odata.test.js +++ b/test/odata.test.js @@ -12,20 +12,20 @@ describe('cap/samples - Bookshop APIs', () => { 'odata-version': '4.0', }) expect(headers['content-type']).to.match(/application\/xml/) - expect(data).to.contain('') - expect(data).to.contain('') + expect(data).to.contain('') + expect(data).to.contain('') }) - it('serves ListOfBooks?$expand=genre,currency', async () => { + it('serves Books?$expand=genre,currency', async () => { const Mystery = { ID: 16, name: 'Mystery', descr: null, parent_ID: 10 } const Romance = { ID: 15, name: 'Romance', descr: null, parent_ID: 10 } const USD = { code: 'USD', name: 'US Dollar', descr: null, symbol: '$' } - const { data } = await GET `/browse/ListOfBooks ${{ + const { data } = await GET `/admin/Books ${{ params: { $search: 'Po', $select: `title,author`, $expand:`genre,currency` }, }}` expect(data.value).to.containSubset([ - { ID: 251, title: 'The Raven', author: 'Edgar Allen Poe', genre:Mystery, currency:USD }, - { ID: 252, title: 'Eleonora', author: 'Edgar Allen Poe', genre:Romance, currency:USD }, + { ID: 251, title: 'The Raven', author_ID: 150, genre:Mystery, currency:USD }, + { ID: 252, title: 'Eleonora', author_ID: 150, genre:Romance, currency:USD }, ]) })