def notes_get(**kwargs): """ # RAML START # description: Query notes stored in Nevernote queryParameters: query_type: displayName: Query Type type: string description: Query by 'note_id' example: "note_id" required: true query: displayName: Query type: integer description: Query term - what is the 'node_id' you are looking up? example: 43 required: true # RAML END # """ try: validate(kwargs) except jsonschema.exceptions.ValidationError as e: return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, f"Error validating input: {e.args[0]}") # Attempt to create new accounts query_type = kwargs["query_type"] query = kwargs["query"] # Construct search_string based on query_type if query_type == "note_id": search_string = "notes.id" else: return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, "Invalid 'query_type' specified") try: sql_command = f""" SELECT notes.id as note_id, notes.title as note_title, notes.body as note_body, to_char(created, 'YYYY-MM-DD HH24:MI:SS') as created, to_char(last_modified, 'YYYY-MM-DD HH24:MI:SS') as last_modified, tag FROM notes JOIN junction_notes_tags ON notes.id = junction_notes_tags.note_id WHERE {search_string} = %s; """ query_result = sql_select_query(sql_command, [query]) response = query_result except psycopg2.DatabaseError: return http_response.http_failure_response( 500, http_response.InternalResponseCode.database_error, "Database could not fulfil request") except KeyError: return http_response.http_failure_response( 404, http_response.InternalResponseCode.basic_info_empty, "Resource not found") return http_response.http_success_response(200, response)
def documentation(): try: if request.method == "GET": return send_file('documentation/api_documentation.html') else: raise InvalidFlaskRoute("Invalid Request Type.") except InvalidFlaskRoute as e: return http_response.http_failure_response( 404, http_response.InternalResponseCode.invalid_request_type, str(e) )
def default(): try: if request.method == "GET": return json.dumps({"message": "Hello World!"}) else: raise InvalidFlaskRoute("Invalid Request Type.") except InvalidFlaskRoute as e: return http_response.http_failure_response( 404, http_response.InternalResponseCode.invalid_request_type, str(e) )
def notes(): try: if request.method == "GET": return notes_get(**params_to_dict(request.args)) elif request.method == "POST": return notes_post(**request.json) else: raise InvalidFlaskRoute("Invalid Request Type.") except InvalidFlaskRoute as e: return http_response.http_failure_response( 404, http_response.InternalResponseCode.invalid_request_type, str(e) )
def notebooks_post(**kwargs): """ # RAML START # description: Create or Delete notebooks queryParameters: action: displayName: Action type: string description: User ID for the user whom made the click. example: "create" required: true notebook_id: displayName: Notebook ID type: integer description: ID of the notebook you would like to perform operations on. Required for 'delete' action. example: "25" required: false title: displayName: Notebook Title type: string description: Title of the notebook you would like to perform operations on. Required for 'create' action. example: "Shopping Lists" required: false # RAML END # """ try: validate(kwargs) except jsonschema.exceptions.ValidationError as e: return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, f"Error validating input: {e.args[0]}") try: action = kwargs['action'] # Create a notebook and return a notebook id if action == "create": if 'title' not in kwargs: return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, "'title' not specified") title = kwargs['title'] sql_command = """ INSERT INTO notebooks(title) VALUES (%s) RETURNING id; """ sql_params = [title] result = sql_insert_query(sql_command, sql_params) notebook_id = result[0]['id'] response = {'notebook_id': notebook_id} # Delete a notebook by notebook id elif action == "delete": # Ensure we have all the params we need required_params = ['notebook_id'] if not all(param in kwargs for param in required_params): return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, f"Required parameters missing. Need: {required_params}") notebook_id = kwargs['notebook_id'] sql_command = """ DELETE FROM notebooks WHERE id = %s; """ sql_params = [notebook_id] rows_deleted = sql_delete_query(sql_command, sql_params) if rows_deleted != 1: raise ValueError( f"Anticipated 'rows_deleted' to equal '1'. Got '{rows_deleted}' instead." ) response = {} # Invalid action specified else: return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, "Invalid 'action' specified") except (IndexError, KeyError, ValueError): return http_response.http_failure_response( 500, http_response.InternalResponseCode.invalid_database_response, "Invalid result returned from database") except psycopg2.DatabaseError: return http_response.http_failure_response( 500, http_response.InternalResponseCode.database_error, "Database could not fulfil request") return http_response.http_success_response(200, response)
def notebooks_get(**kwargs): """ # RAML START # description: Query notebooks stored in Nevernote queryParameters: query_type: displayName: Query Type type: string description: Query by 'notebook_id' or 'notebook_title' example: "notebook_id" required: true query: displayName: Query type: string or integer description: Query term - id or title to search for example: "'grocery_notebook' or '2'" required: true tag: displayName: Tag type: string description: Filter by a 'tag' value example: "funny" required: false # RAML END # """ try: validate(kwargs) except jsonschema.exceptions.ValidationError as e: return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, f"Error validating input: {e.args[0]}") # Attempt to create new accounts query_type = kwargs["query_type"] query = kwargs["query"] # Construct search_string based on query_type if query_type == "notebook_id": search_string = "notebooks.id" elif query_type == "notebook_title": search_string = "notebooks.title" else: return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, "Invalid 'query_type' specified") # Add extra logic if filtering by tags tag_sql_string = "" if 'tag' in kwargs: tag_sql_string = """ JOIN """ try: sql_command = f""" SELECT notebook_id, notes.id as note_id, notebooks.title as notebook_title, notes.title as note_title, to_char(created, 'YYYY-MM-DD HH24:MI:SS') as created, to_char(last_modified, 'YYYY-MM-DD HH24:MI:SS') as last_modified, tag FROM notebooks JOIN junction_notebooks_notes ON junction_notebooks_notes.notebook_id = notebooks.id JOIN notes ON junction_notebooks_notes.note_id = notes.id JOIN junction_notes_tags on notes.id = junction_notes_tags.note_id WHERE {search_string} = %s; """ query_result = sql_select_query(sql_command, [query]) if 'tag' in kwargs: tag = kwargs['tag'].lower() filtered_results = [] for result in query_result: if result['tag'] == tag: filtered_results.append(result) query_result = filtered_results response = query_result except psycopg2.DatabaseError: return http_response.http_failure_response( 500, http_response.InternalResponseCode.database_error, "Database could not fulfil request") except KeyError: return http_response.http_failure_response( 404, http_response.InternalResponseCode.basic_info_empty, "Resource not found") return http_response.http_success_response(200, response)
def notes_post(**kwargs): """ # RAML START # description: Create, Delete, or Update notes queryParameters: action: displayName: Action type: string description: The action you would like to perform: "create", "delete", or "update" example: "create" required: true note_id: displayName: Note ID type: integer description: ID of the note you would like to perform operations on. Required for "delete" and "update" actions. example: 43 required: false title: displayName: Note Title type: string description: Title of the note you are performing actions with. Required for "create" action. example: "Shopping List" required: false body: displayName: Note Body type: string description: Text body of the note you are performing actions with. Required for "create" action. example: "Need to buy: eggs, cheeseburgers, coffee, soup." required: false tags: displayName: Tags type: array items: type: string description: List of tags (strings) to associate with the note. Required for "create" action example: "bdebenon" required: false notebook_id: displayName: Notebook ID type: integer description: Notebook ID of the note you are associating a note with. Required for "create" action. example: 32 required: false # RAML END # """ try: validate(kwargs) except jsonschema.exceptions.ValidationError as e: return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, f"Error validating input: {e.args[0]}") try: action = kwargs['action'] # Create a note and return a note id if action == "create": # Ensure we have all the inputs we need required_params = ['title', 'body', 'tags', 'notebook_id'] if not all(param in kwargs for param in required_params): return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, f"Required parameters missing. Need: {required_params}") title = kwargs['title'] body = kwargs['body'] tags = kwargs['tags'] notebook_id = kwargs['notebook_id'] # Get timestamp time_stamp = time() time_stamp = datetime.fromtimestamp(time_stamp).strftime( '%Y-%m-%d %H:%M:%S') # Add note to db sql_command = """ INSERT INTO notes(title, body, created, last_modified) VALUES (%s, %s, %s, %s) RETURNING id; """ sql_params = [title, body, time_stamp, time_stamp] result = sql_insert_query(sql_command, sql_params) note_id = result[0]['id'] response = {'note_id': note_id} # Add junction between note and notebook sql_command = f""" INSERT INTO junction_notebooks_notes(notebook_id, note_id) VALUES (%s, %s) ON CONFLICT DO NOTHING; """ sql_params = [notebook_id, note_id] result = sql_insert_query(sql_command, sql_params) # Add each tag to db sql_command = f""" INSERT INTO tags(tag) VALUES {insert_placeholders([[tag.lower()] for tag in tags])} ON CONFLICT DO NOTHING; """ sql_params = [tag.lower() for tag in tags] result = sql_insert_query(sql_command, sql_params) # Add junction between tags and note_id node_id_tag_pairs = [(note_id, tag.lower()) for tag in tags] sql_command = f""" INSERT INTO junction_notes_tags(note_id, tag) VALUES {insert_placeholders(node_id_tag_pairs)} ON CONFLICT DO NOTHING; """ sql_params = [ item for sublist in node_id_tag_pairs for item in sublist ] result = sql_insert_query(sql_command, sql_params) # Delete a note by note id elif action == "delete": required_params = ['note_id'] if not all(param in kwargs for param in required_params): return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, f"Required parameters missing. Need: {required_params}") note_id = kwargs['note_id'] sql_command = """ DELETE FROM notes WHERE id = %s; """ sql_params = [note_id] rows_deleted = sql_delete_query(sql_command, sql_params) if rows_deleted != 1: raise ValueError( f"Anticipated 'rows_deleted' to equal '1'. Got '{rows_deleted}' instead." ) response = {} # Update a note by id elif action == "update": required_params = ['note_id'] if not all(param in kwargs for param in required_params): return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, f"Required parameters missing. Need: {required_params}") note_id = kwargs['note_id'] any_required_params = ['body', 'tags'] if not any(param in kwargs for param in any_required_params): return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, f"Required parameters missing. Need at least one of the following: {any_required_params}" ) body = (kwargs['body'] if 'body' in kwargs else None) tags = (kwargs['tags'] if 'tags' in kwargs else None) # Update 'body' if needed if body: sql_command = """ UPDATE notes SET body = %s WHERE id = %s; """ sql_params = [body, note_id] result = sql_update_query(sql_command, sql_params) # Update 'tags' if needed if tags: node_id_tag_pairs = [(note_id, tag.lower()) for tag in tags] sql_command = f""" -- Delete old tags from junction table DELETE FROM junction_notes_tags WHERE note_id = %s; -- Create new tags if not already present INSERT INTO tags(tag) VALUES {insert_placeholders([[tag.lower()] for tag in tags])} ON CONFLICT DO NOTHING; -- Create links in junction table between tag and note_id INSERT INTO junction_notes_tags(note_id, tag) VALUES {insert_placeholders(node_id_tag_pairs)} ON CONFLICT DO NOTHING; """ sql_params = [note_id] # note to delete sql_params.extend([tag.lower() for tag in tags]) # tags list sql_params.extend([ x for sub in node_id_tag_pairs for x in sub ]) # flattened pairs list result = sql_update_query(sql_command, sql_params) response = {} # Invalid action specified else: return http_response.http_failure_response( 400, http_response.InternalResponseCode.invalid_dict_parameters, "Invalid 'action' specified") except (IndexError, KeyError, ValueError): return http_response.http_failure_response( 500, http_response.InternalResponseCode.invalid_database_response, "Invalid result returned from database") except psycopg2.DatabaseError: return http_response.http_failure_response( 500, http_response.InternalResponseCode.database_error, "Database could not fulfil request") return http_response.http_success_response(200, response)