Skip to content
This repository has been archived by the owner on Jul 14, 2023. It is now read-only.

Commit

Permalink
feat: early version of draft support (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
gregorwolf authored Nov 22, 2020
1 parent 1777789 commit 9e9af3c
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 39 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@
.eslintcache
node_modules/
.idea/
__tests__/__assets__/cap-proj/package*
*-env.json
18 changes: 18 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"port": 9229
},
{
"type": "node",
"request": "launch",
"name": "cds run pg",
"args": ["run"],
"cwd": "${workspaceFolder}/__tests__/__assets__/cap-proj",
"program": "${workspaceFolder}/node_modules/@sap/cds/bin/cds.js",
"console": "integratedTerminal"
},
{
"type": "node",
"request": "launch",
"name": "cds run sqlite",
"args": ["run", "--in-memory"],
"cwd": "${workspaceFolder}/__tests__/__assets__/cap-proj",
"program": "${workspaceFolder}/node_modules/@sap/cds/bin/cds.js",
"console": "integratedTerminal"
}
]
}
6 changes: 2 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,15 @@ All notable changes to this project will be documented in this file. See [standa

### 0.0.24 (2020-11-17)


### Features

* add multi schema feature ([#44](https://github.com/sapmentors/cds-pg/issues/44)) ([2eb6835](https://github.com/sapmentors/cds-pg/commit/2eb6835bcdaef2f37039eb5be5bad4f4cd5e50f2))
- add multi schema feature ([#44](https://github.com/sapmentors/cds-pg/issues/44)) ([2eb6835](https://github.com/sapmentors/cds-pg/commit/2eb6835bcdaef2f37039eb5be5bad4f4cd5e50f2))

### 0.0.23 (2020-11-16)


### Features

* support SAP CP CF PG Hyperscaler Service ([#43](https://github.com/sapmentors/cds-pg/issues/43)) ([c0ba1dd](https://github.com/sapmentors/cds-pg/commit/c0ba1dde81fe5c30a9e1fec9ba6b7d71c51ac3cb))
- support SAP CP CF PG Hyperscaler Service ([#43](https://github.com/sapmentors/cds-pg/issues/43)) ([c0ba1dd](https://github.com/sapmentors/cds-pg/commit/c0ba1dde81fe5c30a9e1fec9ba6b7d71c51ac3cb))

### 0.0.22 (2020-11-13)

Expand Down
2 changes: 2 additions & 0 deletions __tests__/__assets__/cap-proj/db/data/csw-TypeChecks.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ID,type_String,type_LargeString
5e4ca9ef-7c4c-4b22-8e85-7cadefa02c94,Guía del autoestopista galáctico,"At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat."
18 changes: 18 additions & 0 deletions __tests__/__assets__/cap-proj/default-env-template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"VCAP_SERVICES": {
"postgres-local": [
{
"name": "postgres",
"label": "postgres",
"tags": ["database"],
"credentials": {
"host": "localhost",
"port": "5432",
"database": "beershop",
"user": "postgres",
"password": "postgres"
}
}
]
}
}
15 changes: 15 additions & 0 deletions __tests__/__assets__/cap-proj/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"cds": {
"requires": {
"db": {
"kind": "database"
},
"database": {
"impl": "../../../",
"model": [
"srv"
]
}
}
}
}
6 changes: 6 additions & 0 deletions __tests__/__assets__/cap-proj/rest-client-test/draft.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
### Create new TypeChecksWithDraft
POST http://localhost:4004/beershop/TypeChecksWithDraft
Accept:application/json;odata.metadata=minimal;IEEE754Compatible=true
Content-Type:application/json;charset=UTF-8;IEEE754Compatible=true

{}
9 changes: 6 additions & 3 deletions __tests__/__assets__/cap-proj/srv/beershop-service.cds
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ using {csw} from '../db/schema';

service BeershopService {

entity Beers as projection on csw.Beers;
entity Breweries as projection on csw.Brewery;
entity TypeChecks as projection on csw.TypeChecks;
entity Beers as projection on csw.Beers;
entity Breweries as projection on csw.Brewery;
entity TypeChecks as projection on csw.TypeChecks;

@odata.draft.enabled
entity TypeChecksWithDraft as projection on csw.TypeChecks;
}
64 changes: 63 additions & 1 deletion __tests__/__assets__/test.sql
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,40 @@ CREATE TABLE csw_TypeChecks (
PRIMARY KEY(ID)
);

CREATE TABLE DRAFT_DraftAdministrativeData (
DraftUUID VARCHAR(36) NOT NULL,
CreationDateTime TIMESTAMP,
CreatedByUser VARCHAR(256),
DraftIsCreatedByMe BOOLEAN,
LastChangeDateTime TIMESTAMP,
LastChangedByUser VARCHAR(256),
InProcessByUser VARCHAR(256),
DraftIsProcessedByMe BOOLEAN,
PRIMARY KEY(DraftUUID)
);

CREATE TABLE BeershopService_TypeChecksWithDraft_drafts (
ID VARCHAR(36) NOT NULL,
type_Boolean BOOLEAN NULL,
type_Int32 INTEGER NULL,
type_Int64 BIGINT NULL,
type_Decimal DECIMAL(2, 1) NULL,
type_Double NUMERIC(30, 15) NULL,
type_Date DATE NULL,
type_Time TIME NULL,
type_DateTime TIMESTAMP NULL,
type_Timestamp TIMESTAMP NULL,
type_String VARCHAR(5000) NULL,
type_Binary CHAR(100) NULL,
type_LargeBinary BYTEA NULL,
type_LargeString TEXT NULL,
IsActiveEntity BOOLEAN,
HasActiveEntity BOOLEAN,
HasDraftEntity BOOLEAN,
DraftAdministrativeData_DraftUUID VARCHAR(36) NOT NULL,
PRIMARY KEY(ID)
);

CREATE VIEW BeershopService_Beers AS SELECT
Beers_0.ID,
Beers_0.name,
Expand Down Expand Up @@ -59,4 +93,32 @@ CREATE VIEW BeershopService_TypeChecks AS SELECT
TypeChecks_0.type_Binary,
TypeChecks_0.type_LargeBinary,
TypeChecks_0.type_LargeString
FROM csw_TypeChecks AS TypeChecks_0
FROM csw_TypeChecks AS TypeChecks_0;

CREATE VIEW BeershopService_TypeChecksWithDraft AS SELECT
TypeChecks_0.ID,
TypeChecks_0.type_Boolean,
TypeChecks_0.type_Int32,
TypeChecks_0.type_Int64,
TypeChecks_0.type_Decimal,
TypeChecks_0.type_Double,
TypeChecks_0.type_Date,
TypeChecks_0.type_Time,
TypeChecks_0.type_DateTime,
TypeChecks_0.type_Timestamp,
TypeChecks_0.type_String,
TypeChecks_0.type_Binary,
TypeChecks_0.type_LargeBinary,
TypeChecks_0.type_LargeString
FROM csw_TypeChecks AS TypeChecks_0;

CREATE VIEW BeershopService_DraftAdministrativeData AS SELECT
DraftAdministrativeData.DraftUUID,
DraftAdministrativeData.CreationDateTime,
DraftAdministrativeData.CreatedByUser,
DraftAdministrativeData.DraftIsCreatedByMe,
DraftAdministrativeData.LastChangeDateTime,
DraftAdministrativeData.LastChangedByUser,
DraftAdministrativeData.InProcessByUser,
DraftAdministrativeData.DraftIsProcessedByMe
FROM DRAFT_DraftAdministrativeData AS DraftAdministrativeData
43 changes: 43 additions & 0 deletions __tests__/lib/pg/service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ describe.each([
expect(response.text.includes(expectedBeersEntitySet)).toBeTruthy()
})

test('List of entities exposed by the service', async () => {
const response = await request.get('/beershop/')

expect(response.status).toStrictEqual(200)
expect(response.body.value.length).toStrictEqual(4)
})

describe('odata: GET -> sql: SELECT', () => {
beforeEach(async () => {
await deploy(this._model, {}).to(this._dbProperties)
Expand Down Expand Up @@ -180,6 +187,42 @@ describe.each([
})
})

describe('odata: GET on Draft enabled Entity -> sql: SELECT', () => {
beforeEach(async () => {
await deploy(this._model, {}).to(this._dbProperties)
})
test('odata: entityset TypeChecksWithDraft -> select all', async () => {
const response = await request.get('/beershop/TypeChecksWithDraft')
expect(response.status).toStrictEqual(200)
})
test('odata: entityset TypeChecksWithDraft -> select all and count', async () => {
const response = await request.get('/beershop/TypeChecksWithDraft?$count=true')
expect(response.status).toStrictEqual(200)
expect(response.body['@odata.count']).toEqual(1)
})
test('odata: entityset TypeChecksWithDraft -> select like Fiori Elements UI', async () => {
const response = await request.get(
'/beershop/TypeChecksWithDraft?$count=true&$expand=DraftAdministrativeData&$filter=(IsActiveEntity%20eq%20false%20or%20SiblingEntity/IsActiveEntity%20eq%20null)&$select=HasActiveEntity,ID,IsActiveEntity,type_Boolean,type_Date,type_Int32&$skip=0&$top=30'
)
expect(response.status).toStrictEqual(200)
expect(response.body['@odata.count']).toEqual(1)
})
test('odata: create new entityset TypeChecksWithDraft -> create like Fiori Elements UI', async () => {
const response = await request
.post('/beershop/TypeChecksWithDraft')
.send(JSON.stringify({}))
.set('Accept', 'application/json;odata.metadata=minimal;IEEE754Compatible=true')
.set('Content-Type', 'application/json;charset=UTF-8;IEEE754Compatible=true')
// Creates:
// sql > SELECT * FROM BeershopService_TypeChecksWithDraft_drafts ALIAS_1 WHERE ID = $1
// values > [ 'c436a286-6d1e-44ad-9630-b09e55b9a61e' ]
// But this fails with:
// The key 'ID' does not exist in the given entity
// the column is created with lowercase id
expect(response.status).toStrictEqual(201)
})
})

describe('odata: POST -> sql: INSERT', () => {
beforeEach(async () => {
await deploy(this._model, {}).to(this._dbProperties)
Expand Down
23 changes: 0 additions & 23 deletions lib/pg/sql-builder/ReferenceBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,6 @@ class PGReferenceBuilder extends ReferenceBuilder {
this._outputObj.sql = this._outputObj.sql.join(' ')
return this._outputObj
}

/**
* Override method and add "AS" part do the column name so that it matches the CDS model
* in order to make the mapping simpler we always add the column name as its found in the
* cds model. That way the returning data is directly mapped to the model
* @param {*} refArray
* @override
*/
_parseReference(refArray) {
if (refArray[0].id) {
throw new Error(`${refArray[0].id}: Views with parameters supported only on HANA`)
}

const entity = this._csn && this._csn.definitions[this._options.entityName]
const element = entity && entity.elements[refArray[0]]
if (element && element.elements) {
// REVISIT we assume that structured elements are already unfolded here
this._outputObj.sql.push(refArray.join('_'))
return
}

this._outputObj.sql.push(refArray.map((el) => `${this._quoteElement(el)} AS "${el}" `).join('.'))
}
}

module.exports = PGReferenceBuilder
41 changes: 41 additions & 0 deletions lib/pg/sql-builder/SelectBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,47 @@ class PGSelectBuilder extends SelectBuilder {
Object.defineProperty(this, 'ExpressionBuilder', { value: ExpressionBuilder })
}

/**
* Override method and add "AS" part to the column name so that it matches the CDS model
* in order to make the mapping simpler we always add the column name as its found in the
* cds model. That way the returning data is directly mapped to the model
*/
_columns(noQuoting) {
if (this._obj.SELECT.columns) {
for (let index = 0; index < this._obj.SELECT.columns.length; index++) {
const element = this._obj.SELECT.columns[index]
if (!element.as) {
if (element.ref && element.ref.length) {
this._obj.SELECT.columns[index].as = element.ref[element.ref.length - 1]
}
}
}
} else {
// fill columns from entity definition
// to avoid "does not exist in the given entity" error for a SELECT *
this._obj.SELECT.columns = []
if (this._obj.SELECT.from.ref[0]) {
let name = this._obj.SELECT.from.ref[0].split('_')[0]
if (!name) {
name = this._obj.SELECT.from.ref[0]
}
let entity = this._csn.definitions[name]
if (entity) {
for (var prop in entity.elements) {
if (entity.elements[prop].type !== 'cds.Association') {
let column = {
ref: [prop],
as: prop,
}
this._obj.SELECT.columns.push(column)
}
}
}
}
}
super._columns(noQuoting)
}

/**
* overwriting the where builder to eliminate ref field aliases (x as "x") -
* introduced rightfully via @see this.ReferenceBuilder._parseReference(),
Expand Down
16 changes: 9 additions & 7 deletions lib/pg/utils/columns.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ const getColumns = require('@sap/cds-runtime/lib/db/utils/columns')
* @param {Object} result
*/
const remapColumnNames = (entity, result) => {
const columns = getColumns(entity)
const resultColumns = Object.keys(result)
for (const column of columns) {
if (resultColumns.includes(column.name.toLowerCase())) {
if (column.name.toLowerCase() !== column.name) {
result[column.name] = result[column.name.toLowerCase()]
delete result[column.name.toLowerCase()]
if (typeof entity !== 'undefined') {
const columns = getColumns(entity)
const resultColumns = Object.keys(result)
for (const column of columns) {
if (resultColumns.includes(column.name.toLowerCase())) {
if (column.name.toLowerCase() !== column.name) {
result[column.name] = result[column.name.toLowerCase()]
delete result[column.name.toLowerCase()]
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"test:pg:up": "docker-compose -f __tests__/__assets__/cap-proj/stack.yml up -d",
"test:pg:down": "docker-compose -f __tests__/__assets__/cap-proj/stack.yml down",
"test:as-sqlite": "cd __tests__/__assets__/cap-proj && cds deploy -2 sqlite::memory: --no-save && cds serve all --in-memory",
"test:as-pg": "cd __tests__/__assets__/cap-proj && cp default-env-template.json default-env.json && cds serve all",
"lint": "prettier -c . && eslint '*.{js,ts,tsx}'",
"release": "standard-version"
},
Expand Down

0 comments on commit 9e9af3c

Please sign in to comment.