Skip to content

Added support for MongoDB #19

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Empty file modified LICENSE
100644 → 100755
Empty file.
Empty file modified Procfile
100644 → 100755
Empty file.
Empty file modified README.md
100644 → 100755
Empty file.
27 changes: 27 additions & 0 deletions README_MONGO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Connecting a MongoDB Database

You'll need access to a MongoDB instance. The below steps outline how to do this:

1. Sign in to your IBM Cloud Console, and select the 'Create Resource' button in the top right of the page.
2. Type 'MongoDB' in the search field and select 'Compose for MongoDB' under the 'Data & Analytics' section.
3. Check that the name/region/organization/pricing fields are accurate.
4. Select the 'Create' button in the botton left of the page.
5. Return to the IBM Cloud dashboard and select the newly created service.
6. In the sidebar on the left side of the page, select 'Service Credentials'
7. Select the 'New Credential' button, and then the 'Add' button in the page that appears.

You will also need to run `pip install -r requirements.txt` again to install the required [MongoDB driver](https://github.com/mongodb/mongo-python-driver).

To prepare the application for use in a local environment, use the below steps. Otherwise, skip the steps to move on to deploying the application to IBM Cloud:

1. Make a copy of the 'vcap-local.template.json' file (in the application root directory) as 'vcap-local.json' in the same directory.
2. Copy the contents of the 'uri' field in the credentials generated from earlier into the field (replacing "MONGO_DATABASE_URI").
3. Run the application using `python hello.py`.

Before deploying the application, make sure that it builds successfully by running `python hello.py` from the root directory of the project (where this README document is located). If no errors are shown, run `cf push` to deploy the application. Once the deployment process completes, run `cf bind-service GetStartedPython [SERVICE_NAME]`, where [SERVICE_NAME] is the name of your MongoDB service. Finally, run `cf retage GetStartedPytohon`. Once this finishes, you can access the application using the URL provided in the output from the 'cf push' command from earlier.

## Additional Notes on Changes

The application may work with either Cloudant or MongoDB. However, if both services are available (and at most one is user-provided), **the application will default to MongoDB**. If both services are available as user-provided services, this behavior is **not** guaranteed, and the application may default to either service.

When deployed on IBM Cloud, this application **does** require bound MongoDB services to have some permutation of 'mongodb' in the name. User-provided services (as created with the cf utility) are also acceptable.
99 changes: 88 additions & 11 deletions hello.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,38 +1,99 @@
###############################################################################
# Copyright IBM Corporation 2018 #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. #
# You may obtain a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
###############################################################################
from cloudant import Cloudant
from flask import Flask, render_template, request, jsonify
import atexit
import os
import json
import ssl
import re
from pymongo import MongoClient

app = Flask(__name__, static_url_path='')

db_name = 'mydb'
collection_name = 'mycollection' # MongoDB requires a collection name.
client = None
db = None
vendor_name = None # Record DB vendor to determine which methods are used.

# Allow for any service with some permutation of a vendor name to be used
# (this is mostly to provide a general solution for user-provided services).
mongo_re = re.compile(r'.*[Mm][Oo][Nn][Gg][Oo].*')
cloudant_re = re.compile(r'.*[Cc][Ll][Oo][Uu][Dd][Aa][Nn][Tt].*')

if 'VCAP_SERVICES' in os.environ:
vcap = json.loads(os.getenv('VCAP_SERVICES'))
print('Found VCAP_SERVICES')
if 'cloudantNoSQLDB' in vcap:
creds = vcap['cloudantNoSQLDB'][0]['credentials']

mongo_result = next(iter(filter(lambda key: mongo_re.search(key) != None, vcap)), None)
cloudant_result = next(iter(filter(lambda key: cloudant_re.search(key) != None, vcap)), None)
user_provided_result = next(iter(filter(lambda key: key == 'user-provided', vcap)), None)

# Allow for user-provided services to be used.
if user_provided_result:
if mongo_re.search(vcap[user_provided_result][0]['name']):
mongo_result = user_provided_result
elif cloudant_re.search(vcap[user_provided_result][0]['name']):
cloudant_result = user_provided_result

if mongo_result:
creds = vcap[mongo_result][0]['credentials']
uri = creds['uri']
client = MongoClient(uri, ssl_cert_reqs=ssl.CERT_NONE)
db = client[db_name][collection_name]
vendor_name = 'mongo'
elif cloudant_result:
creds = vcap[cloudant_result][0]['credentials']
user = creds['username']
password = creds['password']
url = 'https://' + creds['host']
client = Cloudant(user, password, url=url, connect=True)
db = client.create_database(db_name, throw_on_exists=False)
vendor_name = 'cloudant'

elif "CLOUDANT_URL" in os.environ:
client = Cloudant(os.environ['CLOUDANT_USERNAME'], os.environ['CLOUDANT_PASSWORD'], url=os.environ['CLOUDANT_URL'], connect=True)
db = client.create_database(db_name, throw_on_exists=False)
vendor_name = 'cloudant'

elif os.path.isfile('vcap-local.json'):
with open('vcap-local.json') as f:
vcap = json.load(f)
print('Found local VCAP_SERVICES')
creds = vcap['services']['cloudantNoSQLDB'][0]['credentials']
user = creds['username']
password = creds['password']
url = 'https://' + creds['host']
client = Cloudant(user, password, url=url, connect=True)
db = client.create_database(db_name, throw_on_exists=False)

mongo_result = next(iter(filter(lambda key: mongo_re.search(key) != None, vcap['services'])), None)
cloudant_result = next(iter(filter(lambda key: cloudant_re.search(key) != None, vcap['services'])), None)

if mongo_result:
#creds = vcap['services']['compose-for-mongodb'][0]['credentials']
creds = vcap['services'][mongo_result][0]['credentials']
uri = creds['uri']
client = MongoClient(uri, ssl_cert_reqs=ssl.CERT_NONE)
db = client[db_name][collection_name]
vendor_name = 'mongo'
elif cloudant_result:
#creds = vcap['services']['cloudantNoSQLDB'][0]['credentials']
creds = vcap['services'][cloudant_result][0]['credentials']
user = creds['username']
password = creds['password']
url = 'https://' + creds['host']
client = Cloudant(user, password, url=url, connect=True)
db = client.create_database(db_name, throw_on_exists=False)
vendor_name = 'cloudant'

# On IBM Cloud Cloud Foundry, get the port number from the environment variable PORT
# When running this app on the local machine, default the port to 8000
Expand All @@ -42,6 +103,12 @@
def root():
return app.send_static_file('index.html')

def get_visitor_cloudant():
return list(map(lambda doc: doc['name'], db))

def get_visitor_mongo():
return list(map(lambda doc: doc['name'], db.find(projection={"_id": False, "count": False})))

# /* Endpoint to greet and add a new visitor to database.
# * Send a POST request to localhost:8000/api/visitors with body
# * {
Expand All @@ -51,11 +118,21 @@ def root():
@app.route('/api/visitors', methods=['GET'])
def get_visitor():
if client:
return jsonify(list(map(lambda doc: doc['name'], db)))
# Call vendor-specific handler.
return jsonify(globals()['get_visitor_' + vendor_name]())
else:
print('No database')
return jsonify([])

def add_visitor_cloudant(data):
my_document = db.create_document(data)
data['_id'] = my_document['_id']
return data

def add_visitor_mongo(data):
data['_id'] = str(db.insert_one(data).inserted_id)
return data

# /**
# * Endpoint to get a JSON array of all the visitors in the database
# * REST API example:
Expand All @@ -72,8 +149,8 @@ def put_visitor():
user = request.json['name']
data = {'name':user}
if client:
my_document = db.create_document(data)
data['_id'] = my_document['_id']
# Call vendor-specific handler.
data = globals()['add_visitor_' + vendor_name](data)
return jsonify(data)
else:
print('No database')
Expand Down
Empty file modified manifest.yml
100644 → 100755
Empty file.
1 change: 1 addition & 0 deletions requirements.txt
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Flask==0.12.2
cloudant==2.4.0
pymongo[tls]==3.7.0
Empty file modified setup.py
100644 → 100755
Empty file.
Empty file modified static/antixss.js
100644 → 100755
Empty file.
Empty file modified static/index.html
100644 → 100755
Empty file.
Empty file modified static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js
100644 → 100755
Empty file.
Empty file modified static/js/lib/jquery.i18n/jquery.i18n.emitter.js
100644 → 100755
Empty file.
Empty file modified static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js
100644 → 100755
Empty file.
Empty file modified static/js/lib/jquery.i18n/jquery.i18n.js
100644 → 100755
Empty file.
Empty file modified static/js/lib/jquery.i18n/jquery.i18n.language.js
100644 → 100755
Empty file.
Empty file modified static/js/lib/jquery.i18n/jquery.i18n.messagestore.js
100644 → 100755
Empty file.
Empty file modified static/js/lib/jquery.i18n/jquery.i18n.parser.js
100644 → 100755
Empty file.
Empty file modified static/styles.css
100644 → 100755
Empty file.
10 changes: 9 additions & 1 deletion vcap-local.template.json
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
},
"label": "cloudantNoSQLDB"
}
],
"compose-for-mongodb": [
{
"credentials": {
"uri": "MONGO_DATABASE_URI"
},
"label": "compose-for-mongodb"
}
]
}
}
}