Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make discord-bot and netlify-functions-ecommerce work with tables #397

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
28184a3
make discord-bot and netlify-functions-ecommerce work with tables
vkarpov15 Aug 13, 2024
9a0610e
soft deletes on discord-bot using create index
vkarpov15 Aug 13, 2024
92ad56b
make typescript-express-reviews work with tables minus skip+limit
vkarpov15 Aug 14, 2024
ea2c775
change discord bot to not use soft deletes, instead deleteOne() on all
vkarpov15 Aug 16, 2024
8955092
Revert "change discord bot to not use soft deletes, instead deleteOne…
vkarpov15 Aug 16, 2024
842f05a
Merge branch 'main' into vkarpov15/tables
vkarpov15 Aug 19, 2024
6961b6c
Revert "Revert "change discord bot to not use soft deletes, instead d…
vkarpov15 Aug 19, 2024
6ff96d1
snake_case all property names re: stargate/data-api#1349
vkarpov15 Aug 21, 2024
79c3e1a
camelcase names re: stargate/data-api#1355
vkarpov15 Aug 25, 2024
321dfbf
Merge branch 'main' into vkarpov15/tables
vkarpov15 Aug 27, 2024
189cde8
working _id queries and save() re: stargate/data-api#1359
vkarpov15 Aug 27, 2024
b64e661
clean up a bunch of unnecessary tables changes
vkarpov15 Aug 28, 2024
dc5c6e4
Merge branch 'main' into vkarpov15/tables
vkarpov15 Sep 10, 2024
ee5dcfc
almost working local tests for sample apps, excluding stargate/data-a…
vkarpov15 Sep 10, 2024
b6306bd
Merge branch 'main' into vkarpov15/tables
vkarpov15 Sep 18, 2024
55e12db
set feature flags on connect re: stargate-mongoose#249
vkarpov15 Sep 18, 2024
d08781f
use [email protected] to test tables
vkarpov15 Sep 19, 2024
5c31a9f
add log output and bump netlify ecommerce to [email protected]
vkarpov15 Sep 19, 2024
92d0283
use [email protected] in reviews
vkarpov15 Sep 19, 2024
2be24e2
Merge branch 'main' into vkarpov15/tables
vkarpov15 Sep 25, 2024
4f477fd
make typescript express reviews tests pass against tables
vkarpov15 Sep 25, 2024
ae819af
add Mongoose studio to typescript-express reviews, Mongoose studio mo…
vkarpov15 Sep 26, 2024
10869dc
make sure to drop tables before creating
vkarpov15 Sep 27, 2024
fe09f86
fix build
vkarpov15 Sep 27, 2024
15f793d
camelcase names re: stargate/data-api#1452
vkarpov15 Sep 30, 2024
2ec1963
Merge branch 'main' into vkarpov15/tables
vkarpov15 Oct 17, 2024
243c15a
rely on ALLOW FILTERING and remove remaining snake_case names
vkarpov15 Oct 17, 2024
c4f5f4e
readme cleanup and add DATA_API_TABLES to .env.example
vkarpov15 Nov 14, 2024
31a0907
use lists and maps for ecommerce instead of JSON string
vkarpov15 Nov 14, 2024
e44385b
clean up unnecessary error logging and make typescript-express-review…
vkarpov15 Nov 14, 2024
5eabd63
test fixes for sorting
vkarpov15 Nov 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions discord-bot/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ DATA_API_URI=http://127.0.0.1:8181/v1/discordbot
DATA_API_AUTH_USERNAME=cassandra
DATA_API_AUTH_PASSWORD=cassandra
DATA_API_AUTH_URL=http://localhost:8081/v1/auth
#Uncomment the following to enable API tables
#DATA_API_TABLES=true

#Fill the ASTRA DB related details only when IS_ASTRA is set to 'true'
#IS_ASTRA=true
Expand Down
4 changes: 2 additions & 2 deletions discord-bot/.env.test
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
DATA_API_URI=http://127.0.0.1:8181/v1/discordbot_test
DATA_API_URI=http://127.0.0.1:8181/v1/demo
DATA_API_AUTH_USERNAME=cassandra
DATA_API_AUTH_PASSWORD=cassandra
DATA_API_AUTH_URL=http://localhost:8081/v1/auth
DATA_API_TABLES=true
2 changes: 1 addition & 1 deletion discord-bot/commands/countdocuments.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const Bot = require('../models/bot');
module.exports = {
data: new SlashCommandBuilder().setName('count').setDescription('counts documents in the database'),
async execute(interaction) {
const num = await Bot.find().then(res => res.length);
const num = await Bot.find({}).then(res => res.length);
console.log(new Date(), 'count', num);
await interaction.reply(num.toString());
}
Expand Down
4 changes: 3 additions & 1 deletion discord-bot/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ module.exports = async function connect() {
};
} else {
uri = process.env.DATA_API_URI;
const featureFlags = process.env.DATA_API_TABLES ? ['Feature-Flag-tables'] : [];
jsonApiConnectOptions = {
username: process.env.DATA_API_AUTH_USERNAME,
password: process.env.DATA_API_AUTH_PASSWORD,
authUrl: process.env.DATA_API_AUTH_URL
authUrl: process.env.DATA_API_AUTH_URL,
featureFlags
};
}
console.log('Connecting to', uri);
Expand Down
2 changes: 1 addition & 1 deletion discord-bot/models/bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const mongoose = require('../mongoose');

const botSchema = new mongoose.Schema({
name: String
});
}, { versionKey: false });

const Bot = mongoose.model('Bot', botSchema);

Expand Down
29 changes: 24 additions & 5 deletions discord-bot/seed.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,30 @@ seed().catch(err => {

async function seed() {
await connect();

const existingCollections = await mongoose.connection.listCollections()
.then(collections => collections.map(c => c.name));
if (!existingCollections.includes(Bot.collection.collectionName)) {
await Bot.createCollection();

if (process.env.DATA_API_TABLES) {
await mongoose.connection.runCommand({
createTable: {
name: 'bots',
definition: {
primaryKey: '_id',
columns: {
_id: {
type: 'text'
},
name: {
type: 'text'
}
}
}
}
});
} else {
const existingCollections = await mongoose.connection.listCollections()
.then(collections => collections.map(c => c.name));
if (!existingCollections.includes(Bot.collection.collectionName)) {
await Bot.createCollection();
}
}

console.log('Done');
Expand Down
7 changes: 5 additions & 2 deletions discord-bot/test/countdocuments.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ const sinon = require('sinon');

describe('countdocuments', function() {
it('returns the number of bot documents', async function() {
await Bot.deleteMany({});
await Bot.create({ name: 'test' });
const docs = await Bot.find({});
for (const doc of docs) {
await Bot.deleteOne({ _id: doc._id });
}
await Bot.insertMany({ name: 'test' });

const interaction = {
reply: sinon.stub()
Expand Down
7 changes: 5 additions & 2 deletions discord-bot/test/createdocument.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ const sinon = require('sinon');

describe('createdocument', function() {
it('inserts a new document', async function() {
await Bot.deleteMany({});
let docs = await Bot.find({});
for (const doc of docs) {
await Bot.deleteOne({ _id: doc._id });
}

const interaction = {
reply: sinon.stub()
Expand All @@ -17,7 +20,7 @@ describe('createdocument', function() {
assert.ok(interaction.reply.calledOnce);
assert.deepEqual(interaction.reply.getCalls()[0].args, ['done!']);

const docs = await Bot.find();
docs = await Bot.find({});
assert.equal(docs.length, 1);
assert.equal(docs[0].name, 'I am a document');
});
Expand Down
33 changes: 29 additions & 4 deletions discord-bot/test/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,43 @@ const { after, before } = require('mocha');
const mongoose = require('../mongoose');

const uri = process.env.DATA_API_URI;
const featureFlags = process.env.DATA_API_TABLES ? ['Feature-Flag-tables'] : [];
const jsonApiConnectOptions = {
username: process.env.DATA_API_AUTH_USERNAME,
password: process.env.DATA_API_AUTH_PASSWORD,
authUrl: process.env.DATA_API_AUTH_URL
featureFlags
};
if (process.env.DATA_API_TABLES) {
console.log('Testing Data API tables');
}

before(async function() {
this.timeout(30000);
await mongoose.connect(uri, jsonApiConnectOptions);
// dropCollection() can be slower
await Bot.db.dropCollection('bots').catch(() => {});
await Bot.createCollection();

if (process.env.DATA_API_TABLES) {
await mongoose.connection.runCommand({
createTable: {
name: 'bots',
definition: {
primaryKey: '_id',
columns: {
_id: {
type: 'text'
},
name: {
type: 'text'
}
}
}
}
});
}

const docs = await Bot.find({});
for (const doc of docs) {
await Bot.deleteOne({ _id: doc._id });
}
});

after(async function() {
Expand Down
6 changes: 4 additions & 2 deletions netlify-functions-ecommerce/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
# false - if not connecting to an AstraDB and connecting to a local/remote jsonapi instead
IS_ASTRA=

#Fill the JSON API related details only when IS_ASTRA is set to 'false'
#Local JSON API URL for example: http://127.0.0.1:8181/v1/ecommerce_test where 'ecommerce_test' is the keyspace name
#Fill the Data API related details only when IS_ASTRA is set to 'false'
#Local Data API URL for example: http://127.0.0.1:8181/v1/ecommerce_test where 'ecommerce_test' is the keyspace name
DATA_API_URI=http://127.0.0.1:8181/v1/ecommerce_test
#Auth URL for example: http://127.0.0.1:8081/v1/auth
DATA_API_AUTH_URL=http://127.0.0.1:8081/v1/auth
#Auth username and password
DATA_API_AUTH_USERNAME=cassandra
DATA_API_AUTH_PASSWORD=cassandra
#Uncomment the following to enable API tables
#DATA_API_TABLES=true

#Fill the ASTRA DB related details only when IS_ASTRA is set to 'true'
#Astra DB API URL
Expand Down
5 changes: 2 additions & 3 deletions netlify-functions-ecommerce/.env.test
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#Fill the Local JSON API related details only when NODE_ENV is set to 'jsonapi'
#Local JSON API URL for example: http://127.0.0.1:8181/v1/ecommerce_test where 'ecommerce_test' is the keyspace name
DATA_API_URI=http://127.0.0.1:8181/v1/ecommerce_test
#Auth URL for example: http://127.0.0.1:8081/v1/auth
DATA_API_AUTH_URL=http://127.0.0.1:8081/v1/auth
DATA_API_URI=http://127.0.0.1:8181/v1/demo
#Auth username and password
DATA_API_AUTH_USERNAME=cassandra
DATA_API_AUTH_PASSWORD=cassandra
DATA_API_TABLES=true

#Fill in Stripe related details if you want to see Stripe integration.
#Otherwise the sample app will bypass Stripe.
Expand Down
7 changes: 4 additions & 3 deletions netlify-functions-ecommerce/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ Other tools include:
Make sure you have a local stargate instance running as described on the [main page](../README.md) of this repo.

## Running This Example
### Setting up .env file to run against JSON API
### Setting up .env file to run against Data API
1. Copy the `.env.example` file to `.env` and fill in the values for the environment variables.
2. Set `IS_ASTRA` to `false`
3. Set `DATA_API_URI` to `http://127.0.0.1:8181/v1/ecommerce_test`
4. Set `DATA_API_AUTH_URI` to `http://127.0.0.1:8081/v1/auth`
5. Set `DATA_API_AUTH_USERNAME` to `cassandra`
6. Set `DATA_API_AUTH_PASSWORD` to `cassandra`
7. Remove `ASTRA_DB_ID`, `ASTRA_DB_REGION`, `ASTRA_DB_KEYSPACE`, `ASTRA_DB_APPLICATION_TOKEN`
8. (Optional) Set `DATA_API_TABLES=true` to make stargate-mongoose set the `Feature-Flag-tables` headers to enable API tables

### Setting up .env file to run against AstraDB
1. Copy the `.env.example` file to `.env` and fill in the values for the environment variables.
Expand All @@ -34,7 +35,7 @@ Make sure you have a local stargate instance running as described on the [main p
1. Run `npm install`
2. Run `npm run seed` to create all collections and insert sample data
3. Run `npm run build` to compile the frontend
4. (Optional) set `STRIPE_SECRET_KEY` to a test Stripe API key in your `.env` file. This will allow you to enable Stripe checkout.
4. (Optional) set `STRIPE_SECRET_KEY` to a test Stripe API key in your `.env` file. This will allow you to enable Stripe checkout. Or set `STRIPE_SECRET_KEY` to `test` to skip Stripe scheckout
5. Run `npm start`
Run `npm run test:smoke` to run a smoke test against `http://127.0.0.1:8888` that creates a cart using [Axios](https://masteringjs.io/axios).
6. Visit `http://127.0.0.1:8888/` to see the UI
Expand Down Expand Up @@ -70,4 +71,4 @@ Using test


8 passing (112ms)
```
```
8 changes: 6 additions & 2 deletions netlify-functions-ecommerce/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ module.exports = async function connect() {
process.env.ASTRA_NAMESPACE
);
jsonApiConnectOptions = {
isAstra: true
isAstra: true,
level: 'fatal'
};
} else {
uri = process.env.DATA_API_URI;
const featureFlags = process.env.DATA_API_TABLES ? ['Feature-Flag-tables'] : [];
jsonApiConnectOptions = {
username: process.env.DATA_API_AUTH_USERNAME,
password: process.env.DATA_API_AUTH_PASSWORD,
authUrl: process.env.DATA_API_AUTH_URL
authUrl: process.env.DATA_API_AUTH_URL,
featureFlags,
level: 'fatal'
};
}
await mongoose.connect(uri, jsonApiConnectOptions);
Expand Down
59 changes: 45 additions & 14 deletions netlify-functions-ecommerce/models.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,29 @@ const productSchema = new mongoose.Schema({
price: Number,
image: String,
description: String
});
}, { versionKey: false });

const Product = mongoose.model('Product', productSchema);

module.exports.Product = Product;

const orderSchema = new mongoose.Schema({
items: [{
_id: false,
productId: { type: mongoose.ObjectId, required: true, ref: 'Product' },
quantity: { type: Number, required: true, validate: v => v > 0 }
}],
items: {
// `items` is stored as an array of JSON strings for compatibility with Data API tables, which do not
// support lists of objects currently.
type: [{
type: String,
get(v) {
return v == null ? v : JSON.parse(v);
},
set(v) {
if (v == null) {
return v;
}
return typeof v === 'string' ? v : JSON.stringify(v);
}
}]
},
total: {
type: Number,
default: 0
Expand All @@ -31,25 +42,45 @@ const orderSchema = new mongoose.Schema({
brand: String,
last4: String
}
}, { optimisticConcurrency: true });
}, { versionKey: false });

const Order = mongoose.model('Order', orderSchema);

module.exports.Order = Order;

const cartSchema = new mongoose.Schema({
items: [{
_id: false,
productId: { type: mongoose.ObjectId, required: true, ref: 'Product' },
quantity: { type: Number, required: true }
}],
items: {
vkarpov15 marked this conversation as resolved.
Show resolved Hide resolved
// `items` is stored as an array of JSON strings for compatibility with Data API tables, which do not
// support lists of objects currently.
type: [{
type: String,
get(v) {
return v == null ? v : JSON.parse(v);
},
set(v) {
if (v == null) {
return v;
}
return typeof v === 'string' ? v : JSON.stringify(v);
}
}]
},
orderId: { type: mongoose.ObjectId, ref: 'Order' },
total: Number,
stripeSessionId: { type: String }
}, { timestamps: true });
}, { versionKey: false, timestamps: false, toObject: { getters: true }, toJSON: { getters: true } });

cartSchema.virtual('numItems').get(function numItems() {
return this.items.reduce((sum, item) => sum + item.quantity, 0);
if (this.items == null) {
return 0;
}
const items = this.items.map(item => {
if (typeof item === 'string') {
return JSON.parse(item);
}
return item;
});
return items.reduce((sum, item) => sum + item.quantity, 0);
});

const Cart = mongoose.model('Cart', cartSchema);
Expand Down
25 changes: 19 additions & 6 deletions netlify-functions-ecommerce/netlify/functions/addToCart.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,25 @@ const handler = async(event) => {
};
}
for (const product of event.body.items) {
const exists = cart.items.find(item => item?.productId?.toString() === product?.productId?.toString());
const exists = cart.items?.find(item => item?.productId?.toString() === product?.productId?.toString());
if (!exists && products.find(p => product?.productId?.toString() === p?._id?.toString())) {
cart.items.push(product);
await cart.save();
cart.items = [
...cart.items,
product
];
} else {
exists.quantity += product.quantity;
await cart.save();
const items = [];
for (let i = 0; i < cart.items.length; ++i) {
const item = cart.items[i];
if (item?.productId?.toString() === product?.productId?.toString()) {
continue;
}
items.push(item);
}
cart.items = [
...items,
{ productId: product.productId, quantity: exists.quantity + product.quantity }
];
}
}

Expand All @@ -47,10 +59,11 @@ const handler = async(event) => {
return { statusCode: 200, body: JSON.stringify(cart) };
} else {
// If no cartId, create a new cart
const cart = await Cart.create({ items: event.body.items });
const [cart] = await Cart.insertMany([{ items: event.body.items }]);
return { statusCode: 200, body: JSON.stringify(cart) };
}
} catch (error) {
console.error(error);
return { statusCode: 500, body: error.toString() };
}
};
Expand Down
1 change: 1 addition & 0 deletions netlify-functions-ecommerce/netlify/functions/getCart.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const handler = async(event) => {
setOptions({ sanitizeFilter: true });
return { statusCode: 200, body: JSON.stringify({ cart }) };
} catch (error) {
console.error(error);
return { statusCode: 500, body: error.toString() };
}
};
Expand Down
Loading
Loading