Skip to content

Commit

Permalink
Merge pull request #112 from oracle/ORDSConcertAppTemplate
Browse files Browse the repository at this point in the history
feat: add the ords-concert-app template
  • Loading branch information
dbtools-antcampo authored Sep 4, 2024
2 parents f4cd0e2 + 3c04875 commit 5bd8447
Show file tree
Hide file tree
Showing 259 changed files with 25,409 additions and 99 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,15 @@ Next, you will be able to open the project in your favorite IDE or Code Editor a

## Templates

The package offers the following templates, which connect to the database using the [oracledb database driver](https://github.com/oracle/node-oracledb):
The package offers the following templates, some of them connect to the database using the [oracledb database driver](https://github.com/oracle/node-oracledb) and other use [Oracle REST Data Services](https://www.oracle.com/database/technologies/appdev/rest.html) endpoints:

- `node-vanilla`: A starter template that uses Node.js, vanilla JavaScript, HTML and CSS. It is built by [vite](https://vitejs.dev/).
- `node-jet`: A starter template that uses Node.js and [Oracle JET](https://www.oracle.com/webfolder/technetwork/jet/index.html). It is built using the [ojet-cli](https://www.npmjs.com/package/@oracle/ojet-cli).
- `node-react`: A starter template that uses Node.js and [React](https://react.dev/). It is built by [vite](https://vitejs.dev/).
- `node-vue`: A starter template that uses Node.js and [Vue.js](https://vuejs.org/). It is built by [vite](https://vitejs.dev/).
- `node-angular`: A starter template that uses Node.js and [Angular](https://angular.dev/). It is built by [Angular CLI](https://github.com/angular/angular-cli). (New in `v1.2.0`)
- `node-react-todo`: A simple task manager template that uses Node.js and [React](https://react.dev/). It demonstrates the use of the database for Create, Read, Update and Delete (CRUD) operations. It is built by [vite](https://vitejs.dev/).
- `ords-remix-jwt-sample`: A full stack Concert Application made with [Remix](https://remix.run/) that showcases the [Oracle REST Data Services](https://www.oracle.com/database/technologies/appdev/rest.html) functionalities. Some extra configuration is required, learn more about it in the `ords-remix-jwt-sample` [Getting Started Guide](/templates/ords-remix-jwt-sample/README.md#getting-started).

Each of the templates include documentation for you to get started with them, as well as NPM scripts for you to use right after generating the application.

Expand Down Expand Up @@ -89,6 +90,7 @@ DB_USER=<my-username>
DB_PASSWORD=<my-password>
...
```
Note: For the `ords-remix-jwt-sample` is expected that you will configurate your own `.env` file folowing the `ords-remix-jwt-sample` [Getting Started Guide](/templates/ords-remix-jwt-sample/README.md#getting-started).

## Examples

Expand Down
141 changes: 85 additions & 56 deletions generators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export default class extends Generator {
const { protocol, hostname, port, serviceName } = retrieveConnectionStringDetailsFromORAFile( path.join( walletPath, 'tnsnames.ora' ) );
this.options.connectionString = generateConnectionString( protocol, hostname, port, serviceName );
}

// Copy files that are common to all of the templates.
this.fs.copyTpl(
this.templatePath( this.options.templateChoice ),
this.destinationPath(),
Expand All @@ -101,9 +101,9 @@ export default class extends Generator {
}
);
this.fs.copy(
this.templatePath( `${this.options.templateChoice}/.gitignore.template` ),
this.destinationPath( '.gitignore' ),
);
this.templatePath(`${ path.dirname( this.options.templateChoice ) }/app/.github`),
this.destinationPath('.github')
)
// This copy of `eslintrc.cjs` should be removed once all templates support eslint v9
this.fs.copy(
this.templatePath( `${this.options.templateChoice}/.eslintrc.cjs` ),
Expand All @@ -119,63 +119,92 @@ export default class extends Generator {
ignoreNoMatch: true
}
);
this.fs.copy(
this.templatePath(`${ path.dirname( this.options.templateChoice ) }/app/.github`),
this.destinationPath('.github')
)
this.fs.copyTpl(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/${ path.basename( this.options.templateChoice ) == 'node-jet' ? 'index-proxied' : 'index' }.cjs` ),
this.destinationPath( 'server/index.cjs' ),
this.options
);
this.fs.copy(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/routes/${this.options.apiConfiguration}.cjs` ),
this.destinationPath( `server/routes/${this.options.apiConfiguration}.cjs` ),
);
this.fs.copy(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/utils/db/**/*` ),
this.destinationPath( 'server/utils/db/' ),
);
this.fs.copy(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/utils/rest-services/${this.options.apiConfiguration}.cjs` ),
this.destinationPath( `server/utils/rest-services/${this.options.apiConfiguration}.cjs` ),
);
this.fs.copyTpl(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/.env.example` ),
this.destinationPath( '.env.example' ),
{
appName: '',
connectionPassword: '',
connectionString: '',
connectionUsername: '',
walletPassword: '',
walletPath: '',
}
);
this.fs.copyTpl(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/.env.example.${ ( 'walletPath' in this.options ) ? 'cloud-wallet' : 'basic' }` ),
this.destinationPath( '.env' ),
this.options
);
this.fs.copy(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/CONTRIBUTING.md` ),
this.destinationPath( 'CONTRIBUTING.md' ),
);

const readme_data = this.fs.read(this.templatePath(`${path.dirname(this.options.templateChoice)}/app/README.md`));

if(this.fs.exists((this.destinationPath('README.md')))){
this.fs.append(this.destinationPath('README.md'), readme_data);
}
else{
/**
* The ORDS Concert App template provides:
* A .gitignore file
* A markdown lint configuration file (.markdownlint.json)
* A .env.example file
* Additionally, the sample app expects that the user configures their development
* environment on their own to provide a better understanding of ords and how the
* app is structured.
* The rest of the files, like utils/* and db/* are also not needed since the sample
* app contains their own mechanisms to talk with the db.
*/
if( this.options.templateChoice.includes('ords-remix-jwt-sample' )){
this.fs.copy(
this.templatePath( `${this.options.templateChoice}/.gitignore` ),
this.destinationPath( '.gitignore' ),
);
this.fs.copy(
this.templatePath( `${this.options.templateChoice}/.markdownlint.jsonc` ),
this.destinationPath( '.markdownlint.jsonc' ),
);
this.fs.copy(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/README.md` ),
this.destinationPath( 'README.md' ),
this.templatePath( `${this.options.templateChoice}/.env.example` ),
this.destinationPath( '.env.example' ),
);
} else {
this.fs.copy(
this.templatePath( `${this.options.templateChoice}/.gitignore.template` ),
this.destinationPath( '.gitignore' ),
);

this.fs.copyTpl(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/${ path.basename( this.options.templateChoice ) == 'node-jet' ? 'index-proxied' : 'index' }.cjs` ),
this.destinationPath( 'server/index.cjs' ),
this.options
);
this.fs.copy(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/routes/${this.options.apiConfiguration}.cjs` ),
this.destinationPath( `server/routes/${this.options.apiConfiguration}.cjs` ),
);
this.fs.copy(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/utils/db/**/*` ),
this.destinationPath( 'server/utils/db/' ),
);
this.fs.copy(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/utils/rest-services/${this.options.apiConfiguration}.cjs` ),
this.destinationPath( `server/utils/rest-services/${this.options.apiConfiguration}.cjs` ),
);
this.fs.copyTpl(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/.env.example` ),
this.destinationPath( '.env.example' ),
{
appName: '',
connectionPassword: '',
connectionString: '',
connectionUsername: '',
walletPassword: '',
walletPath: '',
}
);
this.fs.copyTpl(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/.env.example.${ ( 'walletPath' in this.options ) ? 'cloud-wallet' : 'basic' }` ),
this.destinationPath( '.env' ),
this.options
);
this.fs.copy(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/CONTRIBUTING.md` ),
this.destinationPath( 'CONTRIBUTING.md' ),
);

const readme_data = this.fs.read(this.templatePath(`${path.dirname(this.options.templateChoice)}/app/README.md`));

if(this.fs.exists((this.destinationPath('README.md')))){
this.fs.append(this.destinationPath('README.md'), readme_data);
}
else{
this.fs.copy(
this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/README.md` ),
this.destinationPath( 'README.md' ),
);
}
}
}

end() {
this.log( 'Application generated successfuly. Run the following command: \n\ncd ' + path.join( process.cwd(), this.options.appName ) + '\n');
this.log( 'Application generated successfully. Run the following command: \n\ncd ' + path.join( process.cwd(), this.options.appName ) + '\n');
if(!this.options.templateChoice.includes('ords-remix-jwt-sample')){}
this.log('Please check out the README file to learn how to configurate the ORDS Concert App')
}
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@oracle/create-database-app",
"version": "1.2.1",
"version": "1.3.0",
"description": "Create an Oracle Database Application from a Template",
"author": "Oracle Corporation",
"keywords": [
Expand Down
85 changes: 46 additions & 39 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export default class Generate extends Command {
'template': Flags.string({
char: 't',
description: 'Template to use',
options: ['node-vanilla', 'node-react', 'node-vue', 'node-react-todo', 'node-jet', 'node-angular'],
options: ['node-vanilla', 'node-react', 'node-vue', 'node-react-todo', 'node-jet', 'node-angular', 'ords-remix-jwt-sample'],
multiple: false
}),

Expand Down Expand Up @@ -368,13 +368,24 @@ export default class Generate extends Command {
value: 'node-react-todo',
description: 'This creates a simple Todo app made with ExpressJS as the backend, React as the frontend, and an Oracle Database connection that will be created from the details you provide later...',
},
{
name: 'ords-remix-jwt-sample',
value: 'ords-remix-jwt-sample',
description: 'This creates a fullstack Concert Application made with Remix that leverages the Oracle REST Data Services functionalities. You will need to configurate the application yourself following the getting started guide.',
},
],
default: 'node-vanilla'
},
) : template;

// This represents the config object that will hold all the information that the user has inputted and selected.
let configObject = {
appName,
templateChoice: path.resolve( path.join( __dirname, '..', '..', 'templates', templateChoice ) ),
};

// Ask the user for the database connection type (Either basic connection or a connection using a cloud wallet).
const databaseConnectionType = connectionType === '' ? await select(
const databaseConnectionType = connectionType === '' && templateChoice !== 'ords-remix-jwt-sample' ? await select(
{
message: 'Which database connection type would you like to choose?',
choices: [
Expand All @@ -391,9 +402,6 @@ export default class Generate extends Command {
}
) : connectionType;

// This represents the config object that will hold all the information that the user has inputted and selected.
let configObject;

// If the user has chosen the basic connection type, then we ask for the protocol, hostname, port and service name / SID.
if ( databaseConnectionType === 'basic' ) {

Expand Down Expand Up @@ -458,13 +466,12 @@ export default class Generate extends Command {
}
) : databaseServiceName;
}

// This will be config object for the basic connection type.
configObject = {
appName,
templateChoice: path.resolve( path.join( __dirname, '..', '..', 'templates', templateChoice ) ),
Object.assign(configObject, {
connectionString: generateConnectionString( protocol, hostname, port, serviceValue )
};
} else {
});
} else if( databaseConnectionType === 'walletPath' ) {
let walletPath = '';

if(walletPathValidationResult === true){
Expand Down Expand Up @@ -493,38 +500,38 @@ export default class Generate extends Command {


// This is the config object that represents the wallet connection type.
configObject = {
appName,
templateChoice: path.resolve( path.join( __dirname, '..', '..', 'templates', templateChoice ) ),
walletPath,
walletPassword,
};
Object.assign(configObject, {
walletPath: walletPath,
walletPassword: walletPassword
});
}

// Ask the user for the database connection username.
Object.assign( configObject, {
connectionUsername: databaseUsername === '' ? await input(
{
message: 'What\'s your database username?',
validate ( input ) {
return input.trim().length === 0 ? 'This field cannot be empty!' : true;
}
},
) : databaseUsername
} );
if(templateChoice !== 'ords-remix-jwt-sample'){
// Ask the user for the database connection username.
Object.assign( configObject, {
connectionUsername: databaseUsername === '' ? await input(
{
message: 'What\'s your database username?',
validate ( input ) {
return input.trim().length === 0 ? 'This field cannot be empty!' : true;
}
},
) : databaseUsername
} );

// Ask the user for the database connection password.
Object.assign( configObject, {
connectionPassword: await password(
{
mask: true,
message: 'What\'s your database password?',
validate ( input ) {
return input.trim().length === 0 ? 'This field cannot be empty!' : true;
}
},
)
} );
// Ask the user for the database connection password.
Object.assign( configObject, {
connectionPassword: await password(
{
mask: true,
message: 'What\'s your database password?',
validate ( input ) {
return input.trim().length === 0 ? 'This field cannot be empty!' : true;
}
},
)
} );
}

generateDatabaseApp( configObject );
// TODO: This is the object that holds the application name, template choice, connection details depending on the chosen connection type.
Expand Down
28 changes: 28 additions & 0 deletions templates/ords-remix-jwt-sample/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright (c) 2024, Oracle and/or its affiliates.
# All rights reserved
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/

# We refer to some variables as Autonomous Database specific
# but you can use whichever ORDS URL you want/have as well as the user,
# as long as this user is capable of creating and REST Enabling other schemas.
ADB_ORDS_URL=https://example.com:8080/ords/
ADB_ADMIN_USER=username
ADB_ADMIN_PASSWORD=

# The name of the schema that will be created to host all of the
# ORDS Concert App database objects.
SCHEMA_NAME=ORDS_CONCERT_APP
SCHEMA_PASSWORD=

# Your Auth0 tenant JWT credentials, used by ORDS to validate request to protected endpoints.
JWT_ISSUER=https://my-domain.auth0.com/
JWT_VERIFICATION_KEY=https://my-domain.auth0.com/oauth/token/.well-known/jwks.json
JWT_AUDIENCE=https://concert.sample.app

# Auth0 Authentication app configuration parameters specific of the sample app.
AUTH0_RETURN_TO_URL=http://localhost:3000
AUTH0_CALLBACK_URL=http://localhost:3000/callback
AUTH0_CLIENT_ID=auth0_client_id
AUTH0_CLIENT_SECRET=auth0_client_secret
AUTH0_DOMAIN=my-domain.auth0.com
AUTH0_LOGOUT_URL=https://my-domain.auth0.com/v2/logout
Loading

0 comments on commit 5bd8447

Please sign in to comment.