1+ const cds = require ( '@sap/cds' ) ;
2+ const { Client } = require ( 'pg' ) ;
3+ const fs = require ( 'fs' ) ;
4+ const path = require ( 'path' ) ;
5+ const { getPostgresConfig } = require ( './lib/postgres-config' ) ;
6+
7+ // Helper function to parse CSV files
8+ function parseCSV ( filePath ) {
9+ try {
10+ const content = fs . readFileSync ( filePath , 'utf8' ) ;
11+ const lines = content . trim ( ) . split ( '\n' ) ;
12+ const headers = lines [ 0 ] . split ( ',' ) ;
13+
14+ return lines . slice ( 1 ) . map ( line => {
15+ const values = line . split ( ',' ) ;
16+ const row = { } ;
17+ headers . forEach ( ( header , index ) => {
18+ let value = values [ index ] ;
19+
20+ // Handle quoted values and remove quotes
21+ if ( value . startsWith ( '"' ) && value . endsWith ( '"' ) ) {
22+ value = value . slice ( 1 , - 1 ) ;
23+ }
24+
25+ // Convert empty strings to null
26+ if ( value === '' ) {
27+ value = null ;
28+ }
29+
30+ row [ header ] = value ;
31+ } ) ;
32+ return row ;
33+ } ) ;
34+ } catch ( error ) {
35+ console . warn ( `⚠️ Could not read CSV file ${ filePath } :` , error . message ) ;
36+ return [ ] ;
37+ }
38+ }
39+
40+ async function initializeDatabase ( ) {
41+ try {
42+ console . log ( '🚀 Starting PostgreSQL database initialization...' ) ;
43+
44+ // Direct PostgreSQL connection for initial setup
45+ const client = new Client ( getPostgresConfig ( ) ) ;
46+
47+ await client . connect ( ) ;
48+ console . log ( '✅ Connected to PostgreSQL' ) ;
49+
50+ // Check if tables already exist
51+ const tableCheck = await client . query ( `
52+ SELECT table_name
53+ FROM information_schema.tables
54+ WHERE table_schema = 'public'
55+ AND table_name LIKE 'sap_%'
56+ ` ) ;
57+
58+ if ( tableCheck . rows . length > 0 ) {
59+ console . log ( 'ℹ️ Database already initialized, checking schema migration...' ) ;
60+
61+ // Check if old schema exists and needs migration
62+ const oldAuthorColumn = await client . query ( `
63+ SELECT column_name
64+ FROM information_schema.columns
65+ WHERE table_name = 'sap_capire_bookstore_authors'
66+ AND column_name = 'birth_date'
67+ ` ) ;
68+
69+ const oldBookColumn = await client . query ( `
70+ SELECT column_name
71+ FROM information_schema.columns
72+ WHERE table_name = 'sap_capire_bookstore_books'
73+ AND column_name = 'author'
74+ ` ) ;
75+
76+ if ( oldAuthorColumn . rows . length > 0 || oldBookColumn . rows . length > 0 ) {
77+ console . log ( '🔄 Migrating old schema to new schema...' ) ;
78+
79+ // Drop old tables to recreate with new structure
80+ await client . query ( 'DROP TABLE IF EXISTS sap_capire_bookstore_books CASCADE' ) ;
81+ await client . query ( 'DROP TABLE IF EXISTS sap_capire_bookstore_authors CASCADE' ) ;
82+
83+ console . log ( '🏗️ Creating new database schema...' ) ;
84+ await createTables ( ) ;
85+ } else {
86+ console . log ( '✅ Schema is up to date' ) ;
87+ }
88+ } else {
89+ console . log ( '🏗️ Creating database schema...' ) ;
90+ await createTables ( ) ;
91+ }
92+
93+ async function createTables ( ) {
94+
95+ // Create Authors table first (referenced by Books)
96+ await client . query ( `
97+ CREATE TABLE IF NOT EXISTS sap_capire_bookstore_authors (
98+ id VARCHAR(36) PRIMARY KEY,
99+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
100+ created_by VARCHAR(255),
101+ modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
102+ modified_by VARCHAR(255),
103+ name VARCHAR(255),
104+ date_of_birth DATE,
105+ nationality VARCHAR(255),
106+ biography TEXT
107+ )
108+ ` ) ;
109+
110+ // Create Books table
111+ await client . query ( `
112+ CREATE TABLE IF NOT EXISTS sap_capire_bookstore_books (
113+ id VARCHAR(36) PRIMARY KEY,
114+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
115+ created_by VARCHAR(255),
116+ modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
117+ modified_by VARCHAR(255),
118+ title VARCHAR(255),
119+ author_id VARCHAR(36),
120+ genre VARCHAR(255),
121+ price DECIMAL(10, 2),
122+ currency_code VARCHAR(3) DEFAULT 'USD',
123+ stock INTEGER DEFAULT 0,
124+ description TEXT,
125+ publisher VARCHAR(255),
126+ published_at DATE,
127+ isbn VARCHAR(13),
128+ FOREIGN KEY (author_id) REFERENCES sap_capire_bookstore_authors(id)
129+ )
130+ ` ) ;
131+
132+ // Create Currencies table
133+ await client . query ( `
134+ CREATE TABLE IF NOT EXISTS sap_common_currencies (
135+ code VARCHAR(3) PRIMARY KEY,
136+ symbol VARCHAR(5),
137+ minor_unit INTEGER,
138+ name VARCHAR(255),
139+ descr VARCHAR(255)
140+ )
141+ ` ) ;
142+
143+ console . log ( '✅ Database schema created' ) ;
144+ }
145+
146+ // Load sample data from CSV files
147+ console . log ( '📊 Loading sample data from CSV files...' ) ;
148+
149+ // Load currencies from CSV
150+ const currenciesPath = path . join ( __dirname , 'db/data/sap.common-Currencies.csv' ) ;
151+ const currenciesData = parseCSV ( currenciesPath ) ;
152+
153+ console . log ( `💰 Found ${ currenciesData . length } currencies in CSV` ) ;
154+ for ( const currency of currenciesData ) {
155+ await client . query ( `
156+ INSERT INTO sap_common_currencies (code, symbol, minor_unit, name, descr)
157+ VALUES ($1, $2, $3, $4, $5)
158+ ON CONFLICT (code) DO NOTHING
159+ ` , [ currency . code , currency . symbol , parseInt ( currency . minorUnit ) , currency . name , currency . descr ] ) ;
160+ }
161+
162+ // Load authors from CSV first (due to foreign key constraint)
163+ const authorsPath = path . join ( __dirname , 'db/data/sap.capire.bookstore-Authors.csv' ) ;
164+ const authorsData = parseCSV ( authorsPath ) ;
165+
166+ console . log ( `✍️ Found ${ authorsData . length } authors in CSV` ) ;
167+ for ( const author of authorsData ) {
168+ await client . query ( `
169+ INSERT INTO sap_capire_bookstore_authors
170+ (id, name, date_of_birth, nationality, biography, created_by)
171+ VALUES ($1, $2, $3, $4, $5, $6)
172+ ON CONFLICT (id) DO NOTHING
173+ ` , [
174+ author . ID ,
175+ author . name ,
176+ author . dateOfBirth ,
177+ author . nationality ,
178+ author . biography ,
179+ author . createdBy || 'system'
180+ ] ) ;
181+ }
182+
183+ // Load books from CSV
184+ const booksPath = path . join ( __dirname , 'db/data/sap.capire.bookstore-Books.csv' ) ;
185+ const booksData = parseCSV ( booksPath ) ;
186+
187+ console . log ( `📚 Found ${ booksData . length } books in CSV` ) ;
188+ for ( const book of booksData ) {
189+ await client . query ( `
190+ INSERT INTO sap_capire_bookstore_books
191+ (id, title, author_id, genre, price, currency_code, stock, description, publisher, published_at, isbn, created_by)
192+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
193+ ON CONFLICT (id) DO NOTHING
194+ ` , [
195+ book . ID ,
196+ book . title ,
197+ book . author_ID ,
198+ book . genre ,
199+ parseFloat ( book . price ) ,
200+ book . currency_code ,
201+ parseInt ( book . stock ) ,
202+ book . description ,
203+ book . publisher ,
204+ book . publishedAt ,
205+ book . isbn ,
206+ book . createdBy || 'system'
207+ ] ) ;
208+ }
209+
210+ await client . end ( ) ;
211+
212+ // Test CDS connection
213+ console . log ( '� Testing CDS connection...' ) ;
214+ const db = await cds . connect . to ( 'db' ) ;
215+ const bookCount = await db . run ( 'SELECT COUNT(*) as count FROM sap_capire_bookstore_books' ) ;
216+ console . log ( `✅ Database initialization completed! Found ${ bookCount [ 0 ] . count } books.` ) ;
217+ await db . disconnect ( ) ;
218+
219+ console . log ( '🎉 Database ready!' ) ;
220+ process . exit ( 0 ) ;
221+
222+ } catch ( error ) {
223+ console . error ( '❌ Database initialization failed:' , error . message ) ;
224+ process . exit ( 1 ) ;
225+ }
226+ }
227+
228+ // Run initialization
229+ initializeDatabase ( ) ;
0 commit comments