def setup(author, location): """Save metadata values.""" with app.app_context(): # save data in db get_db().insert({ "type": "metadata", "author": author, "location": location }) click.echo("Metadata saved!")
def test_create_admin(test_app, cli_runner, click_cli): db = get_db() nb_users = len(db.search(Query().type == "user")) cli_runner.invoke(click_cli, ["create-admin", "__username__"], input="password\npassword") # need to reconnect to db because it has been modified by different processes # so the connection needs to be updated for new changes db = get_db(force_reconnect=True) assert nb_users + 1 == len(db.search(Query().type == "user")) assert len( db.search(Query().type == "user" and Query().username == "__username__"))
def pocket_fixture(test_app, mocked_responses): """Sets up pocket key and mocked responses for testing pocket sync When using this fixture, all calls to https://getpocket.com/v3/get will succeed and return a single article whose url is https://example.com. """ with test_app.app_context(): db = get_db() mocked_responses.add( responses.POST, "https://getpocket.com/v3/oauth/authorize", json={ "access_token": "5678defg-5678-defg-5678-defg56", "username": "******" }) # fake /get response from pocket API mocked_responses.add(responses.POST, "https://getpocket.com/v3/get", json={ 'status': 1, 'complete': 1, 'list': { '3088163616': { 'given_url': 'https://example.com', 'status': '0', 'resolved_url': 'https://example.com', 'excerpt': 'Lorem ipsum', 'is_article': '1', }, }, }) pocket_key = { "type": "pocket_key", "consumer_key": "1234-abcd1234abcd1234abcd1234", "code": "dcba4321-dcba-4321-dcba-4321dc", } db.insert(pocket_key) return pocket_key
def test_create_admin_small_password_fails(test_app, cli_runner, click_cli): cli_runner.invoke(click_cli, ["create-admin", "__username__"], input="short\nshort") db = get_db() assert not len( db.search(Query().type == "user" and Query().username == "__username__"))
def auth(api_key): with app.app_context(): db = get_db() pocket = Query() request_data = { "consumer_key": api_key, "redirect_uri": "https://getpocket.com", } resp = requests.post( "https://getpocket.com/v3/oauth/request", json=request_data, headers={ "X-Accept": "application/json", "Content-Type": "application/json", }, ) new_data = { "type": "pocket_key", "consumer_key": api_key, "code": resp.json()["code"], } if db.search(pocket.type == "pocket_key"): db.update(new_data, pocket.type == "pocket_key") else: db.insert(new_data) click.echo( f"Allow archivy_pocket to retrieve data to your pocket account " f"by visiting https://getpocket.com/auth/authorize?request_token={resp.json()['code']}" f"&redirect_uri=https://getpocket.com")
def test_dataobj_edit_hook(test_app, hooks_cli_runner, note_fixture, client): client.put(f"/api/dataobjs/{note_fixture.id}", json={"content": "Updated note content"}) edit_message = get_db().search(Query().type == "edit_message")[0] assert f"Changes made to content of {note_fixture.title}." == edit_message[ "content"]
def test_app(): """Instantiate the app for each test with its own temporary data directory Each test using this fixture will use its own db.json and its own data directory, and then delete them. """ # create a temporary file to isolate the database for each test global _app if _app is None: _app = create_click_web_app(cli, cli.cli, app) app_dir = tempfile.mkdtemp() _app.config["INTERNAL_DIR"] = app_dir _app.config["USER_DIR"] = app_dir data_dir = os.path.join(app_dir, "data") os.mkdir(data_dir) _app.config["TESTING"] = True _app.config["WTF_CSRF_ENABLED"] = False # This setups a TinyDB instance, using the `app_dir` temporary # directory defined above # Required so that `flask.current_app` can be called in data.py and # models.py # See https://flask.palletsprojects.com/en/1.1.x/appcontext/ for more # information. with _app.app_context(): _ = get_db() user = {"username": "******", "password": "******"} User(**user).insert() yield _app # close and remove the temporary database shutil.rmtree(app_dir)
def complete(): with app.app_context(): db = get_db() try: pocket = db.search(Query().type == "pocket_key")[0] except: click.echo("Key not found") return auth_data = { "consumer_key": pocket["consumer_key"], "code": pocket["code"] } resp = requests.post( "https://getpocket.com/v3/oauth/authorize", json=auth_data, headers={ "X-Accept": "application/json", "Content-Type": "application/json", }, ) db.update( operations.set("access_token", resp.json()["access_token"]), Query().type == "pocket_key", ) click.echo( "Successfully completed auth process, you can now run archivy pocket sync to load the data" )
def test_create_admin_small_password_fails(test_app, cli_runner, click_cli): cli_runner.invoke(cli, ["create-admin", "__username__"], input="short\nshort", env={"ARCHIVY_DATA_DIR": test_app.config["APP_PATH"]}) db = get_db() assert not len( db.search(Query().type == "user" and Query().username == "__username__"))
def sync(force): with app.app_context(): db = get_db() # update pocket dictionary pocket = db.search(Query().type == "pocket_key")[0] pocket_data = { "consumer_key": pocket["consumer_key"], "access_token": pocket["access_token"], "sort": "newest", } # get date of latest call to pocket api since = datetime(1970, 1, 1) create_dir("pocket") already_saved = set() for post in get_items(path="pocket/", structured=False): date = datetime.strptime(post["date"].replace("-", "/"), "%x") already_saved.add(post["url"]) since = max(date, since) if since != datetime(1970, 1, 1) and not force: since = datetime.timestamp(since) pocket_data["since"] = since bookmarks = requests.post("https://getpocket.com/v3/get", json=pocket_data).json() # api spec: https://getpocket.com/developer/docs/v3/retrieve # for some reason, if the `list` attribute is empty it returns a list instead of a dict. if not len(bookmarks["list"]): click.echo("No new bookmarks.") else: for pocket_bookmark in bookmarks["list"].values(): url = pocket_bookmark.get("resolved_url", pocket_bookmark["given_url"]) if int(pocket_bookmark["status"] ) != 2 and url not in already_saved: bookmark = DataObj( url=url, date=datetime.now(), type="pocket_bookmark", path="pocket", ) try: bookmark.process_bookmark_url() click.echo(f"Saving {bookmark.title}...") bookmark.insert() except: click.echo( f"Could not save {bookmark.url} - website may already be down." ) click.echo("Done!")
def edit_user(): form = forms.UserForm() if form.validate_on_submit(): db = get_db() db.update( { "username": form.username.data, "hashed_password": generate_password_hash(form.password.data) }, doc_ids=[current_user.id]) flash("Information saved!", "success") return redirect("/") form.username.data = current_user.username return render_template("users/edit.html", form=form, title="Edit Profile")
def login(): """ Logs in the API client using [HTTP Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication). Pass in the username and password of your account. """ db = get_db() user = db.search(Query().username == request.authorization["username"]) if (user and check_password_hash(user[0]["hashed_password"], request.authorization["password"])): # user is verified so we can log him in from the db user = User.from_db(user[0]) login_user(user, remember=True) return Response(status=200) return Response(status=401)
def login(): form = forms.UserForm() if form.validate_on_submit(): db = get_db() user = db.search((Query().username == form.username.data) & (Query().type == "user")) if user and check_password_hash(user[0]["hashed_password"], form.password.data): user = User.from_db(user[0]) login_user(user, remember=True) flash("Login successful!", "success") next_url = request.args.get("next") return redirect(next_url or "/") flash("Invalid credentials", "error") return redirect("/login") return render_template("users/login.html", form=form, title="Login")
def insert(self): """Inserts the model from the database""" if not self.password: return False hashed_password = generate_password_hash(self.password) db = helpers.get_db() if db.search((Query().type == "user") & (Query().username == self.username)): return False db_user = { "username": self.username, "hashed_password": hashed_password, "is_admin": self.is_admin, "type": "user", } helpers.load_hooks().on_user_create(self) return db.insert(db_user)
def test_initialization(test_app, cli_runner, click_cli): conf_path = os.path.join(test_app.config["USER_DIR"], "config.yml") try: # conf shouldn't exist open(conf_path) assert False except FileNotFoundError: pass old_data_dir = test_app.config["USER_DIR"] with cli_runner.isolated_filesystem(): # create user, localhost, and don't use ES res = cli_runner.invoke( click_cli, ["init"], input="\nn\ny\nusername\npassword\npassword\n\n") assert "Config successfully created" in res.output # verify user was created assert len(get_db().search(Query().type == "user" and Query().username == "username")) # verify dataobj creation works assert DataObj(type="note", title="Test note").insert() assert len(get_items(structured=False)) == 1 conf = open(conf_path).read() # assert defaults are saved assert "PANDOC_HIGHLIGHT_THEME: pygments" in conf assert f"USER_DIR: {test_app.config['USER_DIR']}" in conf assert "HOST: 127.0.0.1" # check ES config not saved assert "ELASTICSEARCH" not in conf # check initialization in random directory # has resulted in change of user dir assert old_data_dir != test_app.config["USER_DIR"]
def add_metadata(dataobj): with app.app_context(): metadata = get_db().search(Query().type == "metadata")[0] dataobj.content += f"Made by {metadata['author']} in {metadata['location']}."
def load_user(user_id): db = helpers.get_db() res = db.get(doc_id=int(user_id)) if res and res["type"] == "user": return User.from_db(res) return None
def test_user_creation_hook(test_app, hooks_cli_runner, user_fixture): creation_message = get_db().search( Query().type == "user_creation_message")[0] assert f"New user {user_fixture.username} created." == creation_message[ "content"]
def test_dataobj_creation_hook(test_app, hooks_cli_runner, note_fixture): creation_message = get_db().search( Query().type == "dataobj_creation_message")[0] assert creation_message[ "content"] == f"New dataobj on {note_fixture.title} with tags: {note_fixture.tags}"
login_manager.login_view = "login" login_manager.init_app(app) app.register_blueprint(api_bp, url_prefix='/api') @login_manager.user_loader def load_user(user_id): db = helpers.get_db() res = db.get(doc_id=int(user_id)) if res and res["type"] == "user": return User.from_db(res) return None app.jinja_options["extensions"].append("jinja2.ext.do") # create admin user if it does not exist with app.app_context(): db = helpers.get_db() user_query = Query() # noqa here because tinydb requires us to explicitly specify is_admin == True if not db.search((user_query.type == "user") & (user_query.is_admin == True)): # noqa: password = token_urlsafe(32) user = User(username="******", password=password, is_admin=True) if user.insert(): app.logger.info(f"""Archivy has created an admin user as it did not exist. Username: '******', password: '******' """) from archivy import routes # noqa: