- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13
feat(db): support MONGO_URI for db creation #254
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
base: dev
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|  | @@ -6,6 +6,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from flask import Flask | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from flask_pymongo import PyMongo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from foca.models.config import MongoConfig, DBConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from urllib.parse import urlparse, urlunparse | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Get logger instance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger = logging.getLogger(__name__) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|  | @@ -123,6 +124,37 @@ def _create_mongo_client( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MongoDB client for Flask application instance. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # If full URI provided, prefer it over component-based construction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| provided_uri = os.environ.get('MONGO_URI') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if provided_uri: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| db_override = os.environ.get('MONGO_DBNAME') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if db_override: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsed = urlparse(provided_uri) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Replace/insert path component with overridden db name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new_path = f"/{db_override}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| provided_uri = urlunparse(( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsed.scheme, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsed.netloc, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new_path, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsed.params, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsed.query, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsed.fragment, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # In case of any parsing issues, fall back to using the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # provided URI as-is; PyMongo will validate later. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
      Comment on lines
    
      +144
     to 
      +147
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (bug_risk): Avoid broad exception handling for URI parsing. Catching only relevant exceptions like ValueError will prevent unrelated errors from being hidden. 
        Suggested change
       
 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, given that FOCA will manage error handling gracefully, I don't think the  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're absolutely right—silently catching all exceptions can mask real issues and make debugging harder. Since FOCA will handle errors gracefully, you have two solid options: 
 try:
    parsed = urlparse(provided_uri)
    new_path = f"/{db_override}"
    provided_uri = urlunparse((
        parsed.scheme,
        parsed.netloc,
        new_path,
        parsed.params,
        parsed.query,
        parsed.fragment,
    ))
except ValueError:
    logger.warning(
        "Failed to parse MONGO_URI for DBNAME override; using original URI."
    )Both approaches are valid, but logging a warning is more user-friendly if you expect misconfiguration to happen. If you prefer simplicity and trust downstream error handling, removing the block is fine too. Let me know if you'd like a specific code suggestion for either approach! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app.config['MONGO_URI'] = provided_uri | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mongo = PyMongo(app) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.info( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Registered database '{db}' using provided MONGO_URI.".format( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| db=os.environ.get('MONGO_DBNAME', db) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return mongo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
      Comment on lines
    
      +148
     to 
      +155
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider logging the actual URI or its components for traceability. Including more details from the URI (excluding sensitive information) in the log will improve traceability and aid in debugging connection problems. 
        Suggested change
       
 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that logging is very useful, but the suggested implementation is quite clunky. See whether you can sanitize the  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're absolutely right—logging should be both informative and clean. You can sanitize the  Here's a suggested approach: from urllib.parse import urlparse
app.config['MONGO_URI'] = provided_uri
mongo = PyMongo(app)
parsed = urlparse(provided_uri)
# Sanitize URI for logging (omit username and password)
host_info = parsed.hostname or 'unknown-host'
port_info = parsed.port or 'default-port'
db_name = os.environ.get('MONGO_DBNAME', db)
uri_scheme = parsed.scheme or 'mongodb'
uri_path = parsed.path.lstrip('/') or db_name
logger.info(
    f"Registered database '{db_name}' using {uri_scheme}://{host_info}:{port_info}/{uri_path} (sanitized)."
)This will ensure the log is both readable and safe, and uses f-strings for clarity. Let me know if you'd like further refinements! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Fall back to legacy component-based construction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auth = '' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user = os.environ.get('MONGO_USERNAME') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if user is not None and user != "": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -81,6 +81,29 @@ def test_register_mongodb_no_database(): | |
| assert isinstance(res, MongoConfig) | ||
|  | ||
|  | ||
| def test__create_mongo_client_with_mongo_uri(monkeypatch): | ||
| """When MONGO_URI environment variable IS defined, prefer it.""" | ||
| # SRV style and options should be accepted without modification | ||
| mongo_uri = "mongodb://user:[email protected]:27017,db2.example.com:27017/admin?replicaSet=rs0&ssl=true" | ||
| monkeypatch.setenv("MONGO_URI", mongo_uri) | ||
| app = Flask(__name__) | ||
| res = _create_mongo_client(app) | ||
| assert isinstance(res, PyMongo) | ||
| assert app.config['MONGO_URI'] == mongo_uri | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Missing test for invalid MONGO_URI values. Add a test with an invalid MONGO_URI to confirm the function handles errors and falls back correctly. | ||
|  | ||
|  | ||
| def test__create_mongo_client_with_mongo_uri_and_db_override(monkeypatch): | ||
| """When MONGO_URI and MONGO_DBNAME are both defined, override db name.""" | ||
| mongo_uri = "mongodb://localhost:27017/old_db?retryWrites=true&w=majority" | ||
| monkeypatch.setenv("MONGO_URI", mongo_uri) | ||
| monkeypatch.setenv("MONGO_DBNAME", "new_db") | ||
| app = Flask(__name__) | ||
| res = _create_mongo_client(app) | ||
| assert isinstance(res, PyMongo) | ||
| assert app.config['MONGO_URI'].startswith("mongodb://localhost:27017/new_db") | ||
| assert "retryWrites=true" in app.config['MONGO_URI'] | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Missing test for MONGO_DBNAME override when MONGO_URI has no path. Add a test case where MONGO_URI lacks a database path and MONGO_DBNAME is set to verify correct URI construction. | ||
|  | ||
|  | ||
| def test_register_mongodb_no_collections(): | ||
| """Register MongoDB database without any collections""" | ||
| app = Flask(__name__) | ||
|  | ||
Uh oh!
There was an error while loading. Please reload this page.