diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/Procfile b/Procfile old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/README_MONGO.md b/README_MONGO.md new file mode 100755 index 0000000..1dbe4be --- /dev/null +++ b/README_MONGO.md @@ -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. diff --git a/hello.py b/hello.py old mode 100644 new mode 100755 index 9218339..7b7177b --- a/hello.py +++ b/hello.py @@ -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 @@ -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 # * { @@ -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: @@ -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') diff --git a/manifest.yml b/manifest.yml old mode 100644 new mode 100755 diff --git a/requirements.txt b/requirements.txt old mode 100644 new mode 100755 index 735f649..ca3c284 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ Flask==0.12.2 cloudant==2.4.0 +pymongo[tls]==3.7.0 diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 diff --git a/static/antixss.js b/static/antixss.js old mode 100644 new mode 100755 diff --git a/static/index.html b/static/index.html old mode 100644 new mode 100755 diff --git a/static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js b/static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js old mode 100644 new mode 100755 diff --git a/static/js/lib/jquery.i18n/jquery.i18n.emitter.js b/static/js/lib/jquery.i18n/jquery.i18n.emitter.js old mode 100644 new mode 100755 diff --git a/static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js b/static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js old mode 100644 new mode 100755 diff --git a/static/js/lib/jquery.i18n/jquery.i18n.js b/static/js/lib/jquery.i18n/jquery.i18n.js old mode 100644 new mode 100755 diff --git a/static/js/lib/jquery.i18n/jquery.i18n.language.js b/static/js/lib/jquery.i18n/jquery.i18n.language.js old mode 100644 new mode 100755 diff --git a/static/js/lib/jquery.i18n/jquery.i18n.messagestore.js b/static/js/lib/jquery.i18n/jquery.i18n.messagestore.js old mode 100644 new mode 100755 diff --git a/static/js/lib/jquery.i18n/jquery.i18n.parser.js b/static/js/lib/jquery.i18n/jquery.i18n.parser.js old mode 100644 new mode 100755 diff --git a/static/styles.css b/static/styles.css old mode 100644 new mode 100755 diff --git a/vcap-local.template.json b/vcap-local.template.json old mode 100644 new mode 100755 index d947a20..8b7e74a --- a/vcap-local.template.json +++ b/vcap-local.template.json @@ -9,6 +9,14 @@ }, "label": "cloudantNoSQLDB" } + ], + "compose-for-mongodb": [ + { + "credentials": { + "uri": "MONGO_DATABASE_URI" + }, + "label": "compose-for-mongodb" + } ] } - } \ No newline at end of file +} \ No newline at end of file