Azure Functions Cosmos DB Access - microsoft-campus-community/workshop-shopping-list-bot GitHub Wiki
Prerequisite: This page assumes that you have this repository forked and the functions
folder open in your IDE. Learn more about the Basics of Azure Functions in the previous page.
This page explains how the Azure Functions access the Cosmos DB. This would work with any Mongo DB.
Open the functions/services/cosmosDBService.ts
file in your IDE.
Each time a Function needs to access the database, it will create an object of the CosmosDBService
class with an interface to the database. Each shopping list is uniquely identified by an id named shoppingListID
. Because every request to a Function will only require work on one shopping list, the Function passes the shoppingListID
to the constructor of CosmosDBService
.
In the constructor of CosmosDBService
, a MongoClient
object is created. The MongoClient
is part of the NPM mongodb package. It is passed the URL to the database. This URL is configured through an environment variable with the name SHOPPING_LIST_COSMOSDB
. You can then use the MongoClient
object for an operation on the shopping list identified by shoppingListID
.
Every time the CosmosDBService
wants to access the database, it must establish a connection through the MongoClient
object, execute the operations it wants and close the connection. Opening the connection is done in the connectAndGetCollection()
function.
The addItem
function takes an Item
object and should insert it into the database. Before inserting the Item
, it should determine the correct position for the Item
in the shopping list.
This function to add an item is not yet implemented. It is your task to complete it to learn how to operate on a Mongo DB.
- First, the
addItem
function should check the input and throw an error if it receives an invalid input. Theitem
may not be undefined, anditem.itemName
may not be undefined, nor an empty string.
if (!item || !item.itemName || item.itemName === '') {
throw new Error('Illegal value for item');
}
- You need to find out the position in the shopping list for the item. A new item should be the last item in the shopping list. To determine its position, you need to know the number of items currently in the shopping list. When connecting to the database with our helper function
connectAndGetCollection()
, you get all the items for all shopping lists. With themongodb
API, you can get only the items with theshoppingListID
you want to add the item to. Then you can count how many items fulfill this condition and calculate the position for the new item as follows.
const collection = await this.connectAndGetCollection();
const positionInShoppingList: number = await collection.find({ shoppingListID: this.shoppingListID }).count() + 1;
- Now that you know at what position in the shopping list you should add the item, you can insert it into the database using the
insertOne
function of themongodb
API. The object to insert is theitem
without theitem.id
so that the Mongo DB will automatically generate anid
. The object to insert contains the id of the shopping list; this new item belongs to.
return (await collection.insertOne({
shoppingListID: this.shoppingListID,
item: new ItemDb(item.itemName, item.marked, positionInShoppingList, item.unit)
})).ops[0].item;
- Lastly, you always want to close the database's connection, even if the operation fails. So you wrap the above code in a
try-finally
statement, and in thefinally
part, you close theMongoClient
connection so that the entire code for theaddItem
function looks like the following.
/**
* Adds a new item to the shopping list.
*
* Retrieves the number of items in the shopping list of the {@link shoppingListID}.
* Sets the increases the number by one and sets it as a position in the shopping list for the item to be added.
*
* Precondition: Item and item's name must not be undefined, and item's name must not be an empty string.
*
* Postcondition: The given item is added to the shopping list with the last position in the shopping list, and the added item is returned.
* An error is thrown if a DB API call failed.
* @param item the item to be added.
* @returns the item which was added.
*/
public async addItem(item: Item): Promise<Item> {
if (!item || !item.itemName || item.itemName === '') {
throw new Error('Illegal value for item');
}
try {
const collection = await this.connectAndGetCollection();
const positionInShoppingList: number = await collection.find({ shoppingListID: this.shoppingListID }).count() + 1;
return (await collection.insertOne({
shoppingListID: this.shoppingListID,
item: new ItemDb(item.itemName, item.marked, positionInShoppingList, item.unit)
})).ops[0].item;
} finally {
await this.client.close();
}
}
Now you have an example of how to work with the Mongo DB API. If you want to learn how other operations work, please look at the remaining code in the CosmosDBService
.
This class implements all the access methods to the Mongo DB. However, when creating the MongoClient
, it passes the connection string to a Mongo DB from the environment variable SHOPPING_LIST_COSMOSDB
. So to access the database, some configuration is needed. You will learn how to configure this environment variable in the next part.