Compare commits

...

21 Commits

Author SHA1 Message Date
Christian Georgi
1f8a78fe8a Update 2022-03-23 23:25:04 +01:00
Christian Georgi
7f9474244b Deploy test 2022-03-23 16:58:52 +01:00
dependabot[bot]
cad615a662 Bump @sap/cds from 5.8.3 to 5.8.4
Bumps [@sap/cds](https://cap.cloud.sap/) from 5.8.3 to 5.8.4.

---
updated-dependencies:
- dependency-name: "@sap/cds"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-17 15:07:20 +01:00
Christian Georgi
7101c58920 Use new horizon theme of Fiori 2022-03-02 15:48:58 +01:00
dependabot[bot]
07dc1e88b3 Bump @sap/cds from 5.8.2 to 5.8.3
Bumps [@sap/cds](https://cap.cloud.sap/) from 5.8.2 to 5.8.3.

---
updated-dependencies:
- dependency-name: "@sap/cds"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-02 11:40:49 +01:00
dependabot[bot]
6f8d74dc9a Bump @sap/cds from 5.8.1 to 5.8.2
Bumps [@sap/cds](https://cap.cloud.sap/) from 5.8.1 to 5.8.2.

---
updated-dependencies:
- dependency-name: "@sap/cds"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-01 14:57:33 +01:00
Christian Georgi
ec57c5ea48 Enable dependency updates 2022-03-01 14:53:44 +01:00
Christian Georgi
c040a47279 Merge pull request #326 from MikhailGoncharov/followup-324
Follow-up #324: better version handling
2022-02-21 09:45:44 +01:00
Christian Georgi
30bfd70c49 Update package-lock 2022-02-21 09:42:12 +01:00
Mikhail Goncharov
6fb9581cf1 Update package.json 2022-02-17 10:39:18 +01:00
Mikhail Goncharov
e87d6cdfc5 Update devDependencies 2022-02-17 10:35:35 +01:00
Mikhail Goncharov
29ea2bc2da Better version handling 2022-02-17 10:09:43 +01:00
Heiko Witteborg
12574271ac Merge pull request #324 from MikhailGoncharov/service-api-read-no-keys-if-not-asked
Fixed eagerly read IDs (CAP !== OData)
2022-02-17 08:53:01 +01:00
Mikhail Goncharov
17b5cc1ad2 Merge branch 'main' into service-api-read-no-keys-if-not-asked 2022-02-15 08:56:08 +01:00
Daniel Hutzel
26ca7f54ad Specified files in package.json (#325) 2022-02-15 01:33:02 +01:00
dependabot[bot]
573e78253d Merge pull request #323 from SAP-samples/dependabot/npm_and_yarn/follow-redirects-1.14.8 2022-02-14 12:02:04 +00:00
Mikhail Goncharov
a7511ed677 Sync with cds runtime for next release 2022-02-14 12:06:22 +01:00
dependabot[bot]
f1289b436b Bump follow-redirects from 1.14.7 to 1.14.8
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-14 10:32:09 +00:00
Dr. David A. Kunz
984ea2133b Merge pull request #322 from SAP-samples/fix-change-to-vue3
Changes in Vue 3 and stable version
2022-02-09 10:23:27 +01:00
D065023
179d301acb Use stable URI 2022-02-09 10:12:41 +01:00
D065023
bbf20b1ca3 Change to Vue 3 2022-02-09 10:10:19 +01:00
22 changed files with 9460 additions and 1085 deletions

8
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
versioning-strategy: increase-if-necessary
schedule:
interval: daily

3752
bookshop/app/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

12
bookshop/app/package.json Normal file
View File

@@ -0,0 +1,12 @@
{
"name": "approuter",
"dependencies": {
"@sap/approuter": "^10"
},
"engines": {
"node": "^16"
},
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
}
}

View File

@@ -3,14 +3,15 @@ const $ = sel => document.querySelector(sel)
const GET = (url) => axios.get('/browse'+url)
const POST = (cmd,data) => axios.post('/browse'+cmd,data)
const books = new Vue ({
const books = Vue.createApp ({
el:'#app',
data: {
data() {
return {
list: [],
book: undefined,
order: { quantity:1, succeeded:'', failed:'' }
order: { quantity:1, succeeded:'', failed:'' },
user: {}
}
},
methods: {
@@ -37,12 +38,21 @@ const books = new Vue ({
book.stock = res.data.stock
books.order = { quantity, succeeded: `Successfully ordered ${quantity} item(s).` }
} catch (e) {
books.order = { quantity, failed: e.response.data.error.message }
books.order = { quantity, failed: e.response.data.error ? e.response.data.error.message : e.response.data }
}
}
}
})
}).mount("#app")
// initially fill list of books
books.fetch()
// show user info on request
document.addEventListener('keydown', async (event) => {
if (event.key === 'u') {
try {
books.user = (await axios.get('/user/User')).data
} catch (err) { }
}
})

View File

@@ -5,19 +5,26 @@
<title> Capire Books </title>
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<style>
.hovering tr:hover td { color:cyan; background: #123; cursor: pointer; }
.rating-stars { color:teal }
.succeeded { color:teal }
.failed { color:red }
.user {text-align: end; color: grey;}
</style>
</head>
<body class="small-container", style="margin-top: 70px;">
<div id='app'>
<h1> {{ document.title }} </h1>
<div v-if="user.ID && user.ID !== 'anonymous'" class="user">
<div>User: {{ user.ID }}</div>
<div>Locale: {{ user.locale }}</div>
<div>Tenant: {{ user.tenant }}</div>
</div>
<h1> Capire Books </h1>
<input type="text" placeholder="Search..." @input="search">

19
bookshop/app/xs-app.json Normal file
View File

@@ -0,0 +1,19 @@
{
"authenticationMethod": "route",
"routes": [
{
"source": "^/app/(.*)$",
"target": "$1",
"localDir": ".",
"authenticationType": "xsuaa",
"cacheControl": "no-cache, no-store, must-revalidate"
},
{
"source": "^/(.*)$",
"target": "$1",
"destination": "srv-api",
"authenticationType": "xsuaa",
"csrfProtection": false
}
]
}

136
bookshop/db/src/.hdiconfig Normal file
View File

@@ -0,0 +1,136 @@
{
"file_suffixes": {
"csv": {
"plugin_name": "com.sap.hana.di.tabledata.source"
},
"hdbafllangprocedure": {
"plugin_name": "com.sap.hana.di.afllangprocedure"
},
"hdbanalyticprivilege": {
"plugin_name": "com.sap.hana.di.analyticprivilege"
},
"hdbcalculationview": {
"plugin_name": "com.sap.hana.di.calculationview"
},
"hdbcollection": {
"plugin_name": "com.sap.hana.di.collection"
},
"hdbconstraint": {
"plugin_name": "com.sap.hana.di.constraint"
},
"hdbdropcreatetable": {
"plugin_name": "com.sap.hana.di.dropcreatetable"
},
"hdbflowgraph": {
"plugin_name": "com.sap.hana.di.flowgraph"
},
"hdbfunction": {
"plugin_name": "com.sap.hana.di.function"
},
"hdbgraphworkspace": {
"plugin_name": "com.sap.hana.di.graphworkspace"
},
"hdbhadoopmrjob": {
"plugin_name": "com.sap.hana.di.virtualfunctionpackage.hadoop"
},
"hdbindex": {
"plugin_name": "com.sap.hana.di.index"
},
"hdblibrary": {
"plugin_name": "com.sap.hana.di.library"
},
"hdbmigrationtable": {
"plugin_name": "com.sap.hana.di.table.migration"
},
"hdbprocedure": {
"plugin_name": "com.sap.hana.di.procedure"
},
"hdbprojectionview": {
"plugin_name": "com.sap.hana.di.projectionview"
},
"hdbprojectionviewconfig": {
"plugin_name": "com.sap.hana.di.projectionview.config"
},
"hdbreptask": {
"plugin_name": "com.sap.hana.di.reptask"
},
"hdbresultcache": {
"plugin_name": "com.sap.hana.di.resultcache"
},
"hdbrole": {
"plugin_name": "com.sap.hana.di.role"
},
"hdbroleconfig": {
"plugin_name": "com.sap.hana.di.role.config"
},
"hdbsearchruleset": {
"plugin_name": "com.sap.hana.di.searchruleset"
},
"hdbsequence": {
"plugin_name": "com.sap.hana.di.sequence"
},
"hdbstatistics": {
"plugin_name": "com.sap.hana.di.statistics"
},
"hdbstructuredprivilege": {
"plugin_name": "com.sap.hana.di.structuredprivilege"
},
"hdbsynonym": {
"plugin_name": "com.sap.hana.di.synonym"
},
"hdbsynonymconfig": {
"plugin_name": "com.sap.hana.di.synonym.config"
},
"hdbsystemversioning": {
"plugin_name": "com.sap.hana.di.systemversioning"
},
"hdbtable": {
"plugin_name": "com.sap.hana.di.table"
},
"hdbtabledata": {
"plugin_name": "com.sap.hana.di.tabledata"
},
"hdbtabletype": {
"plugin_name": "com.sap.hana.di.tabletype"
},
"hdbtrigger": {
"plugin_name": "com.sap.hana.di.trigger"
},
"hdbview": {
"plugin_name": "com.sap.hana.di.view"
},
"hdbvirtualfunction": {
"plugin_name": "com.sap.hana.di.virtualfunction"
},
"hdbvirtualfunctionconfig": {
"plugin_name": "com.sap.hana.di.virtualfunction.config"
},
"hdbvirtualpackagehadoop": {
"plugin_name": "com.sap.hana.di.virtualpackage.hadoop"
},
"hdbvirtualpackagesparksql": {
"plugin_name": "com.sap.hana.di.virtualpackage.sparksql"
},
"hdbvirtualprocedure": {
"plugin_name": "com.sap.hana.di.virtualprocedure"
},
"hdbvirtualprocedureconfig": {
"plugin_name": "com.sap.hana.di.virtualprocedure.config"
},
"hdbvirtualtable": {
"plugin_name": "com.sap.hana.di.virtualtable"
},
"hdbvirtualtableconfig": {
"plugin_name": "com.sap.hana.di.virtualtable.config"
},
"properties": {
"plugin_name": "com.sap.hana.di.tabledata.properties"
},
"tags": {
"plugin_name": "com.sap.hana.di.tabledata.properties"
},
"txt": {
"plugin_name": "com.sap.hana.di.copyonly"
}
}
}

View File

@@ -0,0 +1,5 @@
[
"src/gen/**/*.hdbview",
"src/gen/**/*.hdbindex",
"src/gen/**/*.hdbconstraint"
]

98
bookshop/mta.yaml Normal file
View File

@@ -0,0 +1,98 @@
---
_schema-version: '3.1'
ID: capire.bookshop
version: 1.0.0
description: "A simple self-contained bookshop service."
parameters:
enable-parallel-deployments: true
build-parameters:
before-all:
- builder: custom
commands:
- npx -p @sap/cds-dk cds build --production
modules:
- name: bookshop-srv
type: nodejs
path: gen/srv
parameters:
buildpack: nodejs_buildpack
build-parameters:
builder: npm-ci
provides:
- name: srv-api # required by consumers of CAP services (e.g. approuter)
properties:
srv-url: ${default-url}
- name: mtx-api # potentially required by approuter
properties:
mtx-url: ${default-url}
requires:
- name: bookshop-auth
- name: bookshop-db
- name: bookshop-registry
properties:
SUBSCRIPTION_URL: ${protocol}://\${tenant_subdomain}-${default-uri}
SUBSCRIPTION_URL_REPLACEMENT_RULES: [ [ '-srv', '' ] ]
- name: bookshop
type: approuter.nodejs
path: app/ # from cds.env.folders. Consider also cds.env.build.target -> gen/app
parameters:
keep-existing-routes: true
disk-quota: 256M
memory: 256M
requires:
- name: srv-api
group: destinations
properties:
name: srv-api # must be used in xs-app.json as well
url: ~{srv-url}
forwardAuthToken: true
- name: bookshop-auth
- name: mtx-api
group: destinations
properties:
name: mtx-api # must be used in xs-app.json as well
url: ~{mtx-url}
properties:
TENANT_HOST_PATTERN: "^(.*)-${default-uri}"
resources:
- name: bookshop-auth
type: org.cloudfoundry.managed-service
parameters:
service: xsuaa
service-plan: application
path: ./xs-security.json
config:
xsappname: bookshop-${org}-${space}
tenant-mode: shared
- name: bookshop-db
type: org.cloudfoundry.managed-service
parameters:
service: service-manager
service-plan: container
properties:
hdi-service-name: ${service-name}
- name: bookshop-registry
type: org.cloudfoundry.managed-service
requires:
- name: mtx-api
parameters:
service: saas-registry
service-plan: application
config:
xsappname: bookshop-${org}-${space}
appName: bookshop-${org}-${space}
displayName: bookshop
description: A simple CAP project.
category: 'Category'
appUrls:
getDependencies: ~{mtx-api/mtx-url}/mtx/v1/provisioning/dependencies
onSubscription: ~{mtx-api/mtx-url}/mtx/v1/provisioning/tenant/{tenantId}
onSubscriptionAsync: false
onUnSubscriptionAsync: false
callbackTimeoutMillis: 300000

4025
bookshop/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,19 @@
"name": "@capire/bookshop",
"version": "1.0.0",
"description": "A simple self-contained bookshop service.",
"files": [
"app",
"srv",
"db",
"index.cds",
"index.js"
],
"dependencies": {
"@sap/cds": "^5.0.4",
"@sap/cds": "^5",
"@sap/cds-mtx": "^2",
"@sap/xssec": "^3",
"express": "^4.17.1",
"hdb": "^0.19.0",
"passport": "0.4.1"
},
"scripts": {
@@ -16,7 +26,24 @@
"requires": {
"db": {
"kind": "sql"
},
"[production]": {
"db": {
"kind": "hana-mt"
},
"auth": {
"kind": "xsuaa"
},
"multitenancy": true,
"approuter": {
"kind": "cloudfoundry"
}
}
},
"mtx": {
"element-prefix": "Z_",
"namespace-blocklist": [],
"extension-allowlist": []
}
}
}

View File

@@ -0,0 +1,11 @@
@requires : 'authenticated-user'
service UserService {
@odata.singleton
entity User {
ID : String;
locale : String;
tenant : String;
}
}

View File

@@ -0,0 +1,11 @@
const cds = require('@sap/cds');
module.exports = cds.service.impl((srv) => {
srv.on('READ', 'User', ({ user }) => {
return {
ID: user.id,
locale: user.locale,
tenant: user.tenant,
};
});
});

70
bookshop/xs-security.json Normal file
View File

@@ -0,0 +1,70 @@
{
"scopes": [
{
"name": "$XSAPPNAME.admin",
"description": "admin"
},
{
"name": "$XSAPPNAME.MtxDiagnose",
"description": "Diagnose MTX"
},
{
"name": "$XSAPPNAME.mtcallback",
"description": "Subscribe to applications",
"grant-as-authority-to-apps": [
"$XSAPPNAME(application,sap-provisioning,tenant-onboarding)"
]
},
{
"name": "$XSAPPNAME.mtdeployment",
"description": "Deploy applications"
},
{
"name": "$XSAPPNAME.ExtendCDS",
"description": "Extend CDS applications"
},
{
"name": "$XSAPPNAME.ExtendCDSdelete",
"description": "Extend CDS applications with undeployments"
}
],
"attributes": [],
"role-templates": [
{
"name": "admin",
"description": "generated",
"scope-references": [
"$XSAPPNAME.admin"
],
"attribute-references": []
},
{
"name": "MultitenancyAdministrator",
"description": "Administrate multitenant applications",
"scope-references": [
"$XSAPPNAME.MtxDiagnose",
"$XSAPPNAME.mtdeployment",
"$XSAPPNAME.mtcallback"
]
},
{
"name": "ExtensionDeveloper",
"description": "Extend application",
"scope-references": [
"$XSAPPNAME.ExtendCDS"
]
},
{
"name": "ExtensionDeveloperUndeploy",
"description": "Undeploy extension",
"scope-references": [
"$XSAPPNAME.ExtendCDSdelete"
]
}
],
"authorities": [
"$XSAPPNAME.MtxDiagnose",
"$XSAPPNAME.mtdeployment",
"$XSAPPNAME.mtcallback"
]
}

View File

@@ -16,10 +16,10 @@
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-frameOptions="allow"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_horizon"
data-sap-ui-frameOptions="allow"
></script>
<script>
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))

View File

@@ -25,10 +25,10 @@
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-frameOptions="allow"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_horizon"
data-sap-ui-frameOptions="allow"
></script>
<script>
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))

2238
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,7 @@
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0",
"semver": "^7",
"sqlite3": "npm:@mendix/sqlite3@^5"
},
"scripts": {

View File

@@ -4,21 +4,21 @@ const GET = (url) => axios.get('/reviews'+url)
const PUT = (cmd,data) => axios.patch('/reviews'+cmd,data)
const POST = (cmd,data) => axios.post('/reviews'+cmd,data)
const reviews = new Vue ({
const reviews = Vue.createApp ({
el:'#app',
data: {
list: [],
review: undefined,
message: {},
Ratings: Object.entries({
data() {
return {
list: [],
review: undefined,
message: {},
Ratings: Object.entries({
5 : '★★★★★',
4 : '★★★★',
3 : '★★★',
2 : '★★',
1 : '★',
}).reverse()
}).reverse()
}
},
methods: {
@@ -66,7 +66,7 @@ const reviews = new Vue ({
datetime: (d) => d && new Date(d).toLocaleString(),
},
})
}).mount("#app")
// initially fill list of my reviews
reviews.fetch()

View File

@@ -5,7 +5,7 @@
<title> Capire Reviews </title>
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<style>
.hovering tr:hover td { color:cyan; background: #123; cursor: pointer; }
.rating-stars { color:teal }
@@ -18,7 +18,7 @@
<body class="small-container", style="margin-top: 70px;">
<div id='app'>
<h1> {{ document.title }} </h1>
<h1> Capire Reviews </h1>
<input type="text" placeholder="Search..." @input="search">

View File

@@ -32,6 +32,27 @@ describe('Consuming Services locally', () => {
})
})
}).where(`name like`, 'E%')
if (require('semver').gte(cds.version, '5.9.0')) {
expect(authors).to.containSubset([
{
name: 'Emily Brontë',
books: [
{
title: 'Wuthering Heights',
currency: { name: 'British Pound', symbol: '£' },
},
],
},
{
name: 'Edgar Allen Poe',
books: [
{ title: 'The Raven', currency: { name: 'US Dollar', symbol: '$' } },
{ title: 'Eleonora', currency: { name: 'US Dollar', symbol: '$' } },
],
},
])
return
}
expect(authors).to.containSubset([
{
name: 'Emily Brontë',

View File

@@ -35,6 +35,21 @@ describe('Hierarchical Data', ()=>{
))
it ('supports nested reads', async()=>{
if (require('semver').gte(cds.version, '5.9.0')) {
expect (await
SELECT.one.from (Cats, c=>{
c.ID, c.name.as('parent'), c.children (c=>{
c.name.as('child')
})
}) .where ({name:'Cat'})
) .to.eql (
{ ID:101, parent:'Cat', children:[
{ child:'Kitty' },
{ child:'Catwoman' },
]}
)
return
}
expect (await
SELECT.one.from (Cats, c=>{
c.ID, c.name.as('parent'), c.children (c=>{
@@ -50,6 +65,25 @@ describe('Hierarchical Data', ()=>{
})
it ('supports deeply nested reads', async()=>{
if (require('semver').gte(cds.version, '5.9.0')) {
expect (await SELECT.one.from (Cats, c=>{
c.ID, c.name, c.children (
c => { c.name },
{levels:3}
)
}) .where ({name:'Cat'})
) .to.eql (
{ ID:101, name:'Cat', children:[
{ name:'Kitty', children:[
{ name:'Kitty Cat', children:[
{ name:'Aristocat' }, ]}, // level 3
{ name:'Kitty Bat', children:[] }, ]},
{ name:'Catwoman', children:[
{ name:'Catalina', children:[] } ]},
]}
)
return
}
expect (await SELECT.one.from (Cats, c=>{
c.ID, c.name, c.children (
c => { c.name },