Skip to content

Commit

Permalink
[Add]: Movie list and Entry list batch remove endpoints (#2545)
Browse files Browse the repository at this point in the history
  • Loading branch information
kristenmills committed Dec 29, 2019
1 parent 158f731 commit 29be89f
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 4 deletions.
4 changes: 2 additions & 2 deletions flexget/api/app.py
Expand Up @@ -20,7 +20,7 @@

from . import __path__

__version__ = '1.6.0'
__version__ = '1.7.0'

logger = logger.bind(name='api')

Expand Down Expand Up @@ -137,7 +137,7 @@ def response(self, code_or_apierror, description='Success', model=None, **kwargs
code_or_apierror.response_model,
)
},
**kwargs
**kwargs,
)
except TypeError:
# If first argument isn't a class this happens
Expand Down
37 changes: 37 additions & 0 deletions flexget/components/managed_lists/lists/entry_list/api.py
Expand Up @@ -58,6 +58,15 @@ class ObjectsContainer:

entry_lists_entries_return_object = {'type': 'array', 'items': entry_list_entry_base_object}

batch_ids = {'type': 'array', 'items': {'type': 'integer'}, 'uniqueItems': True, 'minItems': 1}

batch_remove_object = {
'type': 'object',
'properties': {'ids': batch_ids},
'required': ['ids'],
'additionalProperties': False,
}


entry_list_object_schema = api.schema_model(
'entry_list_object_schema', ObjectsContainer.entry_list_base_object
Expand All @@ -68,6 +77,9 @@ class ObjectsContainer:
entry_list_return_lists_schema = api.schema_model(
'entry_list_return_lists_schema', ObjectsContainer.entry_list_return_lists
)
entry_list_batch_remove_schema = api.schema_model(
'entry_list.batch_remove_object', ObjectsContainer.batch_remove_object
)

entry_list_parser = api.parser()
entry_list_parser.add_argument('name', help='Filter results by list name')
Expand Down Expand Up @@ -283,3 +295,28 @@ def put(self, list_id, entry_id, session=None):
resp = jsonify(entry.to_dict())
resp.status_code = 201
return resp


@entry_list_api.route('/<int:list_id>/entries/batch')
@api.doc(params={'list_id': 'ID of the list'})
@api.response(NotFoundError)
class EntryListEntriesBatchAPI(APIResource):
@api.response(204)
@api.validate(model=entry_list_batch_remove_schema)
@api.doc(description='Remove multiple entries')
def delete(self, list_id, session=None):
"""Remove multiple entries"""
data = request.json
entry_ids = data.get('ids')
try:
entries = db.get_entries_by_list_id(list_id, entry_ids=entry_ids, session=session)
except NoResultFound:
raise NotFoundError(f'could not find entries in list {list_id}')

for entry in entries:
session.delete(entry)
session.commit()
response = jsonify([])
response.status_code = 204

return response
10 changes: 9 additions & 1 deletion flexget/components/managed_lists/lists/entry_list/db.py
Expand Up @@ -215,10 +215,18 @@ def delete_list_by_id(list_id, session=None):

@with_session
def get_entries_by_list_id(
list_id, start=None, stop=None, order_by='title', descending=False, session=None
list_id,
start=None,
stop=None,
order_by='title',
descending=False,
entry_ids=None,
session=None,
):
logger.debug('querying entries from entry list with id {}', list_id)
query = session.query(EntryListEntry).filter(EntryListEntry.list_id == list_id)
if entry_ids:
query = query.filter(EntryListEntry.id.in_(entry_ids))
if descending:
query = query.order_by(getattr(EntryListEntry, order_by).desc())
else:
Expand Down
38 changes: 38 additions & 0 deletions flexget/components/managed_lists/lists/movie_list/api.py
Expand Up @@ -82,6 +82,15 @@ class ObjectsContainer:

return_identifiers = {'type': 'array', 'items': {'type': 'string'}}

batch_ids = {'type': 'array', 'items': {'type': 'integer'}, 'uniqueItems': True, 'minItems': 1}

batch_remove_object = {
'type': 'object',
'properties': {'ids': batch_ids},
'required': ['ids'],
'additionalProperties': False,
}


input_movie_entry_schema = api.schema_model(
'input_movie_entry', ObjectsContainer.input_movie_entry
Expand All @@ -105,6 +114,10 @@ class ObjectsContainer:
'movie_list.identifiers', ObjectsContainer.return_identifiers
)

movie_list_batch_remove_schema = api.schema_model(
'movie_list.batch_remove_object', ObjectsContainer.batch_remove_object
)

movie_list_parser = api.parser()
movie_list_parser.add_argument('name', help='Filter results by list name')

Expand Down Expand Up @@ -328,3 +341,28 @@ class MovieListIdentifiers(APIResource):
def get(self, session=None):
""" Return a list of supported movie list identifiers """
return jsonify(MovieListBase().supported_ids)


@movie_list_api.route('/<int:list_id>/movies/batch')
@api.doc(params={'list_id': 'ID of the list'})
@api.response(NotFoundError)
class MovieListEntriesBatchAPI(APIResource):
@api.response(204)
@api.validate(model=movie_list_batch_remove_schema)
@api.doc(description='Remove multiple entries')
def delete(self, list_id, session=None):
"""Remove multiple entries"""
data = request.json
movie_ids = data.get('ids')
try:
movies = db.get_movies_by_list_id(list_id, movie_ids=movie_ids, session=session)
except NoResultFound:
raise NotFoundError(f'could not find movies in list {list_id}')

for movie in movies:
session.delete(movie)
session.commit()
response = jsonify([])
response.status_code = 204

return response
10 changes: 9 additions & 1 deletion flexget/components/managed_lists/lists/movie_list/db.py
Expand Up @@ -121,9 +121,17 @@ def to_dict(self):

@with_session
def get_movies_by_list_id(
list_id, start=None, stop=None, order_by='added', descending=False, session=None
list_id,
start=None,
stop=None,
order_by='added',
descending=False,
movie_ids=None,
session=None,
):
query = session.query(MovieListMovie).filter(MovieListMovie.list_id == list_id)
if movie_ids:
query = query.filter(MovieListMovie.id.in_(movie_ids))
if descending:
query = query.order_by(getattr(MovieListMovie, order_by).desc())
else:
Expand Down
34 changes: 34 additions & 0 deletions flexget/tests/api_tests/test_entry_list_api.py
Expand Up @@ -258,6 +258,40 @@ def test_entry_list_entry(self, api_client, schema_match):
errors = schema_match(base_message, data)
assert not errors

def test_entry_list_entries_batch_remove(self, api_client, schema_match):
payload = {'name': 'test_list'}

# Create list
api_client.json_post('/entry_list/', data=json.dumps(payload))

# Add 3 entries to list
for i in range(3):
payload = {'title': f'title {i}', 'original_url': f'http://{i}test.com'}
rsp = api_client.json_post('/entry_list/1/entries/', data=json.dumps(payload))
assert rsp.status_code == 201

# get entries is correct
rsp = api_client.get('/entry_list/1/entries/')
assert rsp.status_code == 200
data = json.loads(rsp.get_data(as_text=True))

errors = schema_match(OC.entry_lists_entries_return_object, data)
assert not errors
assert len(data) == 3

payload = {'ids': [1, 2, 3]}

rsp = api_client.json_delete('entry_list/1/entries/batch', data=json.dumps(payload))
assert rsp.status_code == 204

rsp = api_client.get('/entry_list/1/entries/')
assert rsp.status_code == 200
data = json.loads(rsp.get_data(as_text=True))

errors = schema_match(OC.entry_lists_entries_return_object, data)
assert not errors
assert not data


class TestEntryListPagination:
config = 'tasks: {}'
Expand Down
34 changes: 34 additions & 0 deletions flexget/tests/api_tests/test_movie_list_api.py
Expand Up @@ -322,6 +322,40 @@ def test_identifiers(self, api_client, schema_match):
identifiers = MovieListBase().supported_ids
assert data == identifiers

def test_movie_list_movies_batch_remove(self, api_client, schema_match):
payload = {'name': 'test_list'}

# Create list
api_client.json_post('/movie_list/', data=json.dumps(payload))

# Add 3 entries to list
for i in range(3):
payload = {'movie_name': f'movie {i}', 'movie_year': 2000 + i}
rsp = api_client.json_post('/movie_list/1/movies/', data=json.dumps(payload))
assert rsp.status_code == 201

# get entries is correct
rsp = api_client.get('/movie_list/1/movies/')
assert rsp.status_code == 200
data = json.loads(rsp.get_data(as_text=True))

errors = schema_match(OC.return_movies, data)
assert not errors
assert len(data) == 3

payload = {'ids': [1, 2, 3]}

rsp = api_client.json_delete('movie_list/1/movies/batch', data=json.dumps(payload))
assert rsp.status_code == 204

rsp = api_client.get('/movie_list/1/movies/')
assert rsp.status_code == 200
data = json.loads(rsp.get_data(as_text=True))

errors = schema_match(OC.return_movies, data)
assert not errors
assert not data


class TestMovieListPagination:
config = 'tasks: {}'
Expand Down

0 comments on commit 29be89f

Please sign in to comment.