Skip to content
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

Issue 247 generic asset crud #290

Merged
merged 35 commits into from Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a36eeaa
add a new method to specify an auth context (by callable) and discuss…
nhoening Dec 29, 2021
00d5651
add ACL for GenericAsset
nhoening Dec 29, 2021
63ee1fd
added /dev/generic_assets API, CRUD uses it (passing manual testing)
nhoening Dec 29, 2021
02edb47
fix asset icon in asset view
nhoening Dec 29, 2021
c6a0b50
remove verbs from /api/dev/generic_assets API routes (HTTP method alr…
nhoening Dec 30, 2021
6018004
make CRUD UI tests (mocking the asset API) work
nhoening Dec 30, 2021
e922e39
auth policy: change 'create' to 'create-children' permission (removin…
nhoening Dec 30, 2021
4c6e213
separate fresh tests from others; only generate copies of generic ass…
nhoening Jan 1, 2022
f304a98
add missing mock in another ui test
nhoening Jan 1, 2022
b0fbff6
add unique contraints: on GenericAssetType.name & on GenericAssets fo…
nhoening Jan 1, 2022
3982e0f
improve explanation of our authorization policy implementation
nhoening Jan 1, 2022
2ae8aad
update changelog
nhoening Jan 1, 2022
fdfca9c
improve changelog, also mention temporary API location
nhoening Jan 3, 2022
4c95ff9
comments next to test cases
nhoening Jan 3, 2022
5518c17
create_user function hashes the password
nhoening Jan 3, 2022
0c492a0
more informative validation errror
nhoening Jan 3, 2022
2ab9f5b
allow creation of public Assets in UI
nhoening Jan 3, 2022
f75982c
Fix display of owning account
nhoening Jan 3, 2022
912b81e
better changelog docs
nhoening Jan 3, 2022
7d7e5a1
list sensors on asset page
nhoening Jan 4, 2022
b2479db
do not fail getting latest state if capacity_in_mw is not in attributes
nhoening Jan 4, 2022
850b92d
better default for capacity_in_mw
nhoening Jan 4, 2022
c5ce5a9
Merge branch 'project-9' into issue-247-GenericAsset-CRUD
nhoening Jan 4, 2022
2d463bb
use id in the dict, for sensors lookup
nhoening Jan 4, 2022
a7f1d7f
Suggested fixes to pr 290 (#299)
Flix6x Jan 4, 2022
50248bb
small improvement on locating ea addresses
nhoening Jan 4, 2022
8359502
Describe current state of transition
nhoening Jan 4, 2022
d50c4f4
deprecation notice in old-style asset API documentation
nhoening Jan 4, 2022
133e04b
Fix type annotation
Flix6x Jan 4, 2022
93c9a69
Update documentation
Flix6x Jan 4, 2022
c4262ce
Update v2.0 connection entity addresses in documentation
Flix6x Jan 4, 2022
f9633e5
Correct quickrefs in v2.0 documentation
Flix6x Jan 4, 2022
81d6f6a
Update other v2.0 sensor entity addresses in documentation
Flix6x Jan 5, 2022
70f180c
Fix entity address dates corresponding to older previous domain name
Flix6x Jan 5, 2022
9165e79
Adapt more occurrences of older dates in entity addresses, and switch…
Flix6x Jan 5, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions documentation/changelog.rst
Expand Up @@ -2,11 +2,10 @@
FlexMeasures Changelog
**********************

v0.8.0 | November XX, 2021
v0.8.0 | January XX, 2022
nhoening marked this conversation as resolved.
Show resolved Hide resolved
===========================

.. warning:: Upgrading to this version requires running ``flexmeasures db upgrade`` (you can create a backup first with ``flexmeasures db-ops dump``).
.. warning:: Changes to asset attributes made via the UI or API are not reflected in the new data model until `issue #247 <https://github.com/FlexMeasures/flexmeasures/issues/247>`_ is resolved.

New features
-----------
Expand All @@ -23,6 +22,7 @@ Infrastructure / Support
* Allow plugins to register their custom config settings, so that FlexMeasures can check whether they are set up correctly [see `PR #230 <http://www.github.com/FlexMeasures/flexmeasures/pull/230>`_ and `PR #237 <http://www.github.com/FlexMeasures/flexmeasures/pull/237>`_]
* Add sensor method to obtain just its latest state (excl. forecasts) [see `PR #235 <http://www.github.com/FlexMeasures/flexmeasures/pull/235>`_]
* Migrate attributes of assets, markets and weather sensors to our new sensor model [see `PR #254 <http://www.github.com/FlexMeasures/flexmeasures/pull/254>`_ and `project 9 <http://www.github.com/FlexMeasures/flexmeasures/projects/9>`_]
* Support the new asset model (the business/non-data world) in UI and API [see `PR #251 <http://www.github.com/FlexMeasures/flexmeasures/pull/251>`_ and `PR #290 <http://www.github.com/FlexMeasures/flexmeasures/pulls/290>`_]
nhoening marked this conversation as resolved.
Show resolved Hide resolved


v0.7.1 | November 08, 2021
Expand Down
14 changes: 8 additions & 6 deletions flexmeasures/api/common/schemas/users.py
Expand Up @@ -10,12 +10,6 @@ class AccountIdField(fields.Integer):
Field that represents an account ID. It de-serializes from the account id to an account instance.
"""

def __init__(self, *args, **kwargs):
kwargs["load_default"] = (
lambda: current_user.account if not current_user.is_anonymous else None
)
super().__init__(*args, **kwargs)

def _deserialize(self, account_id: int, attr, obj, **kwargs) -> Account:
account: Account = Account.query.filter_by(id=int(account_id)).one_or_none()
if account is None:
Expand All @@ -25,6 +19,14 @@ def _deserialize(self, account_id: int, attr, obj, **kwargs) -> Account:
def _serialize(self, account: Account, attr, data, **kwargs) -> int:
return account.id

@classmethod
def load_current(cls):
"""
Use this with the load_default arg to __init__ if you want the current user's account
by default.
"""
return current_user.account if not current_user.is_anonymous else None


class UserIdField(fields.Integer):
"""
Expand Down
6 changes: 5 additions & 1 deletion flexmeasures/api/dev/__init__.py
Expand Up @@ -8,9 +8,13 @@ def register_at(app: Flask):
"""This can be used to register FlaskViews."""

from flexmeasures.api.dev.sensors import SensorAPI
from flexmeasures.api.dev.assets import AssetAPI
from flexmeasures.api.dev.sensor_data import post_data as post_sensor_data_impl

SensorAPI.register(app, route_prefix="/api/dev")
dev_api_prefix = "/api/dev"

SensorAPI.register(app, route_prefix=dev_api_prefix)
AssetAPI.register(app, route_prefix=dev_api_prefix)

@app.route("/sensorData", methods=["POST"])
@auth_token_required
Expand Down
86 changes: 86 additions & 0 deletions flexmeasures/api/dev/assets.py
@@ -0,0 +1,86 @@
from flask import current_app
from flask_classful import FlaskView, route
from flask_json import as_json
from webargs.flaskparser import use_kwargs, use_args

from flexmeasures.auth.decorators import permission_required_for_context
from flexmeasures.data.models.user import Account
from flexmeasures.data.models.generic_assets import GenericAsset as AssetModel
from flexmeasures.data.schemas.generic_assets import GenericAssetSchema as AssetSchema
from flexmeasures.api.common.schemas.generic_assets import AssetIdField
from flexmeasures.api.common.schemas.users import AccountIdField
from flexmeasures.data.config import db


asset_schema = AssetSchema()
assets_schema = AssetSchema(many=True)


class AssetAPI(FlaskView):
"""
This API view exposes generic assets.
Under development until it replaces the original Asset API.
"""

route_base = "/generic_assets"

@route("/", methods=["GET"])
@use_kwargs(
{
"account": AccountIdField(
data_key="account_id", load_default=AccountIdField.load_current
),
},
location="query",
)
@permission_required_for_context("read", arg_name="account")
@as_json
def index(self, account: Account):
"""List all assets owned by a certain account."""
return assets_schema.dump(account.generic_assets), 200

@route("/", methods=["POST"])
@permission_required_for_context(
"create-children", arg_loader=AccountIdField.load_current
)
@use_args(AssetSchema())
def post(self, asset_data):
"""Create new asset"""
asset = AssetModel(**asset_data)
db.session.add(asset)
db.session.commit()
return asset_schema.dump(asset), 201

@route("/<id>", methods=["GET"])
@use_kwargs({"asset": AssetIdField(data_key="id")}, location="path")
@permission_required_for_context("read", arg_name="asset")
@as_json
def fetch_one(self, id, asset):
"""Fetch a given asset"""
return asset_schema.dump(asset), 200

@route("/<id>", methods=["PATCH"])
@use_args(AssetSchema(partial=True))
@use_kwargs({"db_asset": AssetIdField(data_key="id")}, location="path")
@permission_required_for_context("update", arg_name="db_asset")
@as_json
def patch(self, asset_data, id, db_asset):
"""Update an asset given its identifier"""
ignored_fields = ["id", "account_id"]
for k, v in [(k, v) for k, v in asset_data.items() if k not in ignored_fields]:
setattr(db_asset, k, v)
db.session.add(db_asset)
db.session.commit()
return asset_schema.dump(db_asset), 200

@route("/<id>", methods=["DELETE"])
@use_kwargs({"asset": AssetIdField(data_key="id")}, location="path")
@permission_required_for_context("delete", arg_name="asset")
@as_json
def delete(self, id, asset):
"""Delete an asset given its identifier"""
asset_name = asset.name
db.session.delete(asset)
db.session.commit()
current_app.logger.info("Deleted asset '%s'." % asset_name)
return {}, 204
10 changes: 6 additions & 4 deletions flexmeasures/api/dev/tests/conftest.py
Expand Up @@ -6,17 +6,19 @@
from flexmeasures.data.models.time_series import Sensor


@pytest.fixture(scope="module", autouse=True)
def setup_api_test_data(db, setup_roles_users):
@pytest.fixture(scope="module")
def setup_api_test_data(db, setup_roles_users, setup_generic_assets):
"""
Set up data for API dev tests.
"""
print("Setting up data for API v2.0 tests on %s" % db.engine)
print("Setting up data for API dev tests on %s" % db.engine)
add_gas_sensor(db, setup_roles_users["Test Prosumer User 2"])


@pytest.fixture(scope="function")
def setup_api_fresh_test_data(fresh_db, setup_roles_users_fresh_db):
def setup_api_fresh_test_data(
fresh_db, setup_roles_users_fresh_db, setup_generic_assets_fresh_db
):
"""
Set up fresh data for API dev tests.
"""
Expand Down