mirror of
https://github.com/Readarr/Readarr
synced 2026-01-04 14:43:42 +01:00
Fixed: Removed unnecessary author data from book endpoint
This commit is contained in:
parent
ce58e6ecdb
commit
a59706ceb4
16 changed files with 190 additions and 49 deletions
|
|
@ -8,6 +8,7 @@ import * as commandNames from 'Commands/commandNames';
|
|||
import { toggleBooksMonitored } from 'Store/Actions/bookActions';
|
||||
import { clearBookFiles, fetchBookFiles } from 'Store/Actions/bookFileActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import { clearEditions, fetchEditions } from 'Store/Actions/editionActions';
|
||||
import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions';
|
||||
import createAllAuthorSelector from 'Store/Selectors/createAllAuthorsSelector';
|
||||
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
||||
|
|
@ -43,11 +44,12 @@ function createMapStateToProps() {
|
|||
(state, { titleSlug }) => titleSlug,
|
||||
selectBookFiles,
|
||||
(state) => state.books,
|
||||
(state) => state.editions,
|
||||
createAllAuthorSelector(),
|
||||
createCommandsSelector(),
|
||||
createUISettingsSelector(),
|
||||
createDimensionsSelector(),
|
||||
(titleSlug, bookFiles, books, authors, commands, uiSettings, dimensions) => {
|
||||
(titleSlug, bookFiles, books, editions, authors, commands, uiSettings, dimensions) => {
|
||||
const book = books.items.find((b) => b.titleSlug === titleSlug);
|
||||
const author = authors.find((a) => a.id === book.authorId);
|
||||
const sortedBooks = books.items.filter((b) => b.authorId === book.authorId);
|
||||
|
|
@ -79,8 +81,8 @@ function createMapStateToProps() {
|
|||
isRefreshingCommand.body.bookId === book.id
|
||||
);
|
||||
|
||||
const isFetching = isBookFilesFetching;
|
||||
const isPopulated = isBookFilesPopulated;
|
||||
const isFetching = isBookFilesFetching || editions.isFetching;
|
||||
const isPopulated = isBookFilesPopulated && editions.isPopulated;
|
||||
|
||||
return {
|
||||
...book,
|
||||
|
|
@ -104,6 +106,8 @@ const mapDispatchToProps = {
|
|||
executeCommand,
|
||||
fetchBookFiles,
|
||||
clearBookFiles,
|
||||
fetchEditions,
|
||||
clearEditions,
|
||||
clearReleases,
|
||||
cancelFetchReleases,
|
||||
toggleBooksMonitored
|
||||
|
|
@ -121,7 +125,8 @@ class BookDetailsConnector extends Component {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!_.isEqual(getMonitoredEditions(prevProps), getMonitoredEditions(this.props)) ||
|
||||
if (prevProps.id !== this.props.id ||
|
||||
!_.isEqual(getMonitoredEditions(prevProps), getMonitoredEditions(this.props)) ||
|
||||
(prevProps.anyReleaseOk === false && this.props.anyReleaseOk === true)) {
|
||||
this.unpopulate();
|
||||
this.populate();
|
||||
|
|
@ -140,12 +145,14 @@ class BookDetailsConnector extends Component {
|
|||
const bookId = this.props.id;
|
||||
|
||||
this.props.fetchBookFiles({ bookId });
|
||||
this.props.fetchEditions({ bookId });
|
||||
}
|
||||
|
||||
unpopulate = () => {
|
||||
this.props.cancelFetchReleases();
|
||||
this.props.clearReleases();
|
||||
this.props.clearBookFiles();
|
||||
this.props.clearEditions();
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -195,6 +202,8 @@ BookDetailsConnector.propTypes = {
|
|||
titleSlug: PropTypes.string.isRequired,
|
||||
fetchBookFiles: PropTypes.func.isRequired,
|
||||
clearBookFiles: PropTypes.func.isRequired,
|
||||
fetchEditions: PropTypes.func.isRequired,
|
||||
clearEditions: PropTypes.func.isRequired,
|
||||
clearReleases: PropTypes.func.isRequired,
|
||||
cancelFetchReleases: PropTypes.func.isRequired,
|
||||
toggleBooksMonitored: PropTypes.func.isRequired,
|
||||
|
|
|
|||
|
|
@ -8,15 +8,25 @@ import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
|||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import BookDetailsHeader from './BookDetailsHeader';
|
||||
|
||||
const selectOverview = createSelector(
|
||||
(state) => state.editions,
|
||||
(editions) => {
|
||||
const monitored = editions.items.find((e) => e.monitored === true);
|
||||
return monitored?.overview;
|
||||
}
|
||||
);
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createBookSelector(),
|
||||
selectOverview,
|
||||
createUISettingsSelector(),
|
||||
createDimensionsSelector(),
|
||||
(book, uiSettings, dimensions) => {
|
||||
(book, overview, uiSettings, dimensions) => {
|
||||
|
||||
return {
|
||||
...book,
|
||||
overview,
|
||||
shortDateFormat: uiSettings.shortDateFormat,
|
||||
isSmallScreen: dimensions.isSmallScreen
|
||||
};
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class EditBookModalContent extends Component {
|
|||
editions
|
||||
} = item;
|
||||
|
||||
const hasFile = statistics ? statistics.bookFileCount : 0;
|
||||
const hasFile = statistics ? statistics.bookFileCount > 0 : false;
|
||||
const errorMessage = getErrorMessage(error, 'Unable to load editions');
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import React, { Component } from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { saveBook, setBookValue } from 'Store/Actions/bookActions';
|
||||
import { clearEditions, fetchEditions } from 'Store/Actions/editionActions';
|
||||
import { saveEditions } from 'Store/Actions/editionActions';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import createBookSelector from 'Store/Selectors/createBookSelector';
|
||||
import selectSettings from 'Store/Selectors/selectSettings';
|
||||
|
|
@ -26,17 +26,14 @@ function createMapStateToProps() {
|
|||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
items
|
||||
error
|
||||
} = editionState;
|
||||
|
||||
book.editions = items;
|
||||
|
||||
const bookSettings = _.pick(book, [
|
||||
'monitored',
|
||||
'anyEditionOk',
|
||||
'editions'
|
||||
'anyEditionOk'
|
||||
]);
|
||||
bookSettings.editions = editionState.items;
|
||||
|
||||
const settings = selectSettings(bookSettings, pendingChanges, saveError);
|
||||
|
||||
|
|
@ -58,10 +55,9 @@ function createMapStateToProps() {
|
|||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
dispatchFetchEditions: fetchEditions,
|
||||
dispatchClearEditions: clearEditions,
|
||||
dispatchSetBookValue: setBookValue,
|
||||
dispatchSaveBook: saveBook
|
||||
dispatchSaveBook: saveBook,
|
||||
dispatchSaveEditions: saveEditions
|
||||
};
|
||||
|
||||
class EditBookModalContentConnector extends Component {
|
||||
|
|
@ -69,20 +65,12 @@ class EditBookModalContentConnector extends Component {
|
|||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.dispatchFetchEditions({ bookId: this.props.bookId });
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
|
||||
this.props.onModalClose();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.dispatchClearEditions();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
|
|
@ -94,6 +82,9 @@ class EditBookModalContentConnector extends Component {
|
|||
this.props.dispatchSaveBook({
|
||||
id: this.props.bookId
|
||||
});
|
||||
this.props.dispatchSaveEditions({
|
||||
id: this.props.bookId
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -114,10 +105,9 @@ EditBookModalContentConnector.propTypes = {
|
|||
bookId: PropTypes.number,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
saveError: PropTypes.object,
|
||||
dispatchFetchEditions: PropTypes.func.isRequired,
|
||||
dispatchClearEditions: PropTypes.func.isRequired,
|
||||
dispatchSetBookValue: PropTypes.func.isRequired,
|
||||
dispatchSaveBook: PropTypes.func.isRequired,
|
||||
dispatchSaveEditions: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { connect } from 'react-redux';
|
|||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import createBookAuthorSelector from 'Store/Selectors/createBookAuthorSelector';
|
||||
import createBookQualityProfileSelector from 'Store/Selectors/createBookQualityProfileSelector';
|
||||
import createBookSelector from 'Store/Selectors/createBookSelector';
|
||||
import createExecutingCommandsSelector from 'Store/Selectors/createExecutingCommandsSelector';
|
||||
|
|
@ -32,11 +33,13 @@ function selectShowSearchAction() {
|
|||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createBookSelector(),
|
||||
createBookAuthorSelector(),
|
||||
createBookQualityProfileSelector(),
|
||||
selectShowSearchAction(),
|
||||
createExecutingCommandsSelector(),
|
||||
(
|
||||
book,
|
||||
author,
|
||||
qualityProfile,
|
||||
showSearchAction,
|
||||
executingCommands
|
||||
|
|
@ -54,7 +57,7 @@ function createMapStateToProps() {
|
|||
const isRefreshingBook = executingCommands.some((command) => {
|
||||
return (
|
||||
(command.name === commandNames.REFRESH_AUTHOR &&
|
||||
command.body.authorId === book.author.id) ||
|
||||
command.body.authorId === book.authorId) ||
|
||||
(command.name === commandNames.REFRESH_BOOK &&
|
||||
command.body.bookId === book.id)
|
||||
);
|
||||
|
|
@ -63,7 +66,7 @@ function createMapStateToProps() {
|
|||
const isSearchingBook = executingCommands.some((command) => {
|
||||
return (
|
||||
(command.name === commandNames.AUTHOR_SEARCH &&
|
||||
command.body.authorId === book.author.id) ||
|
||||
command.body.authorId === book.authorId) ||
|
||||
(command.name === commandNames.BOOK_SEARCH &&
|
||||
command.body.bookIds.includes(book.id))
|
||||
);
|
||||
|
|
@ -71,6 +74,7 @@ function createMapStateToProps() {
|
|||
|
||||
return {
|
||||
...book,
|
||||
author,
|
||||
qualityProfile,
|
||||
showSearchAction,
|
||||
isRefreshingBook,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
|||
import { icons } from 'Helpers/Props';
|
||||
import dimensions from 'Styles/Variables/dimensions';
|
||||
import fonts from 'Styles/Variables/fonts';
|
||||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
||||
import stripHtml from 'Utilities/String/stripHtml';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import BookIndexOverviewInfo from './BookIndexOverviewInfo';
|
||||
|
|
@ -42,10 +43,26 @@ class BookIndexOverview extends Component {
|
|||
|
||||
this.state = {
|
||||
isEditAuthorModalOpen: false,
|
||||
isDeleteAuthorModalOpen: false
|
||||
isDeleteAuthorModalOpen: false,
|
||||
overview: ''
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { id } = this.props;
|
||||
|
||||
// Note that this component is lazy loaded by the virtualised view.
|
||||
// We want to avoid storing overviews for *all* books which is
|
||||
// why it's not put into the redux store
|
||||
const promise = createAjaxRequest({
|
||||
url: `/book/${id}/overview`
|
||||
}).request;
|
||||
|
||||
promise.done((data) => {
|
||||
this.setState({ overview: data.overview });
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
|
|
@ -84,7 +101,6 @@ class BookIndexOverview extends Component {
|
|||
const {
|
||||
id,
|
||||
title,
|
||||
overview,
|
||||
monitored,
|
||||
titleSlug,
|
||||
nextAiring,
|
||||
|
|
@ -118,6 +134,7 @@ class BookIndexOverview extends Component {
|
|||
} = statistics;
|
||||
|
||||
const {
|
||||
overview,
|
||||
isEditAuthorModalOpen,
|
||||
isDeleteAuthorModalOpen
|
||||
} = this.state;
|
||||
|
|
@ -267,7 +284,6 @@ class BookIndexOverview extends Component {
|
|||
BookIndexOverview.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
overview: PropTypes.string.isRequired,
|
||||
monitored: PropTypes.bool.isRequired,
|
||||
titleSlug: PropTypes.string.isRequired,
|
||||
nextAiring: PropTypes.string,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { createAction } from 'redux-actions';
|
||||
import { batchActions } from 'redux-batched-actions';
|
||||
import { createThunk, handleThunks } from 'Store/thunks';
|
||||
import getProviderState from 'Utilities/State/getProviderState';
|
||||
import { updateItem } from './baseActions';
|
||||
import createFetchHandler from './Creators/createFetchHandler';
|
||||
import createHandleActions from './Creators/createHandleActions';
|
||||
import createClearReducer from './Creators/Reducers/createClearReducer';
|
||||
|
|
@ -25,18 +28,39 @@ export const defaultState = {
|
|||
|
||||
export const FETCH_EDITIONS = 'editions/fetchEditions';
|
||||
export const CLEAR_EDITIONS = 'editions/clearEditions';
|
||||
export const SAVE_EDITIONS = 'editions/saveEditions';
|
||||
|
||||
//
|
||||
// Action Creators
|
||||
|
||||
export const fetchEditions = createThunk(FETCH_EDITIONS);
|
||||
export const clearEditions = createAction(CLEAR_EDITIONS);
|
||||
export const saveEditions = createThunk(SAVE_EDITIONS);
|
||||
|
||||
//
|
||||
// Action Handlers
|
||||
|
||||
export const actionHandlers = handleThunks({
|
||||
[FETCH_EDITIONS]: createFetchHandler(section, '/edition')
|
||||
[FETCH_EDITIONS]: createFetchHandler(section, '/edition'),
|
||||
|
||||
[SAVE_EDITIONS]: function(getState, payload, dispatch) {
|
||||
const {
|
||||
id,
|
||||
...otherPayload
|
||||
} = payload;
|
||||
|
||||
const saveData = getProviderState({ id, ...otherPayload }, getState, 'books');
|
||||
|
||||
dispatch(batchActions([
|
||||
...saveData.editions.map((edition) => {
|
||||
return updateItem({
|
||||
id: edition.id,
|
||||
section: 'editions',
|
||||
...edition
|
||||
});
|
||||
})
|
||||
]));
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
|
|
|
|||
15
frontend/src/Store/Selectors/createBookAuthorSelector.js
Normal file
15
frontend/src/Store/Selectors/createBookAuthorSelector.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import createBookSelector from './createBookSelector';
|
||||
|
||||
function createBookAuthorSelector() {
|
||||
return createSelector(
|
||||
createBookSelector(),
|
||||
(state) => state.authors.itemMap,
|
||||
(state) => state.authors.items,
|
||||
(book, authorMap, allAuthors) => {
|
||||
return allAuthors[authorMap[book.authorId]];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createBookAuthorSelector;
|
||||
|
|
@ -1,18 +1,16 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import createBookSelector from './createBookSelector';
|
||||
import createBookAuthorSelector from './createBookAuthorSelector';
|
||||
|
||||
function createBookQualityProfileSelector() {
|
||||
return createSelector(
|
||||
(state) => state.settings.qualityProfiles.items,
|
||||
createBookSelector(),
|
||||
(qualityProfiles, book) => {
|
||||
if (!book) {
|
||||
createBookAuthorSelector(),
|
||||
(qualityProfiles, author) => {
|
||||
if (!author) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return qualityProfiles.find((profile) => {
|
||||
return profile.id === book.author.qualityProfileId;
|
||||
});
|
||||
return qualityProfiles.find((profile) => profile.id === author.qualityProfileId);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ private Book GetEditionInfo(int id, bool getAllEditions)
|
|||
var edition = book.Editions.Value.SingleOrDefault(e => e.ForeignEditionId == id.ToString());
|
||||
|
||||
trimmed.Editions = new List<Edition> { edition };
|
||||
return trimmed;
|
||||
book = trimmed;
|
||||
}
|
||||
|
||||
var authorDict = authors.ToDictionary(x => x.ForeignAuthorId);
|
||||
|
|
|
|||
|
|
@ -52,12 +52,23 @@ public void missing_should_have_author()
|
|||
{
|
||||
EnsureAuthor("14586394", "43765115", "Andrew Hunter Murray", true);
|
||||
|
||||
var result = WantedMissing.GetPaged(0, 15, "releaseDate", "desc");
|
||||
var result = WantedMissing.GetPagedIncludeAuthor(0, 15, "releaseDate", "desc", includeAuthor: true);
|
||||
|
||||
result.Records.First().Author.Should().NotBeNull();
|
||||
result.Records.First().Author.AuthorName.Should().Be("Andrew Hunter Murray");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(1)]
|
||||
public void missing_should_not_have_author()
|
||||
{
|
||||
EnsureAuthor("14586394", "43765115", "Andrew Hunter Murray", true);
|
||||
|
||||
var result = WantedMissing.GetPagedIncludeAuthor(0, 15, "releaseDate", "desc", includeAuthor: false);
|
||||
|
||||
result.Records.First().Author.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(2)]
|
||||
public void cutoff_should_have_monitored_items()
|
||||
|
|
@ -103,12 +114,25 @@ public void cutoff_should_have_author()
|
|||
var author = EnsureAuthor("14586394", "43765115", "Andrew Hunter Murray", true);
|
||||
EnsureBookFile(author, 1, "43765115", Quality.MOBI);
|
||||
|
||||
var result = WantedCutoffUnmet.GetPaged(0, 15, "releaseDate", "desc");
|
||||
var result = WantedCutoffUnmet.GetPagedIncludeAuthor(0, 15, "releaseDate", "desc", includeAuthor: true);
|
||||
|
||||
result.Records.First().Author.Should().NotBeNull();
|
||||
result.Records.First().Author.AuthorName.Should().Be("Andrew Hunter Murray");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(2)]
|
||||
public void cutoff_should_not_have_author()
|
||||
{
|
||||
EnsureProfileCutoff(1, Quality.AZW3);
|
||||
var author = EnsureAuthor("14586394", "43765115", "Andrew Hunter Murray", true);
|
||||
EnsureBookFile(author, 1, "43765115", Quality.MOBI);
|
||||
|
||||
var result = WantedCutoffUnmet.GetPagedIncludeAuthor(0, 15, "releaseDate", "desc", includeAuthor: false);
|
||||
|
||||
result.Records.First().Author.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(1)]
|
||||
public void missing_should_have_unmonitored_items()
|
||||
|
|
|
|||
40
src/NzbDrone.Integration.Test/Client/WantedClient.cs
Normal file
40
src/NzbDrone.Integration.Test/Client/WantedClient.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
using System.Collections.Generic;
|
||||
using Readarr.Api.V1.Books;
|
||||
using Readarr.Http;
|
||||
using RestSharp;
|
||||
|
||||
namespace NzbDrone.Integration.Test.Client
|
||||
{
|
||||
public class WantedClient : ClientBase<BookResource>
|
||||
{
|
||||
public WantedClient(IRestClient restClient, string apiKey, string resource)
|
||||
: base(restClient, apiKey, resource)
|
||||
{
|
||||
}
|
||||
|
||||
public PagingResource<BookResource> GetPagedIncludeAuthor(int pageNumber, int pageSize, string sortKey, string sortDir, string filterKey = null, string filterValue = null, bool includeAuthor = true)
|
||||
{
|
||||
var request = BuildRequest();
|
||||
request.AddParameter("page", pageNumber);
|
||||
request.AddParameter("pageSize", pageSize);
|
||||
request.AddParameter("sortKey", sortKey);
|
||||
request.AddParameter("sortDir", sortDir);
|
||||
|
||||
if (filterKey != null && filterValue != null)
|
||||
{
|
||||
request.AddParameter("filterKey", filterKey);
|
||||
request.AddParameter("filterValue", filterValue);
|
||||
}
|
||||
|
||||
request.AddParameter("includeAuthor", includeAuthor);
|
||||
|
||||
return Get<PagingResource<BookResource>>(request);
|
||||
}
|
||||
|
||||
public List<BookResource> GetBooksInAuthor(int authorId)
|
||||
{
|
||||
var request = BuildRequest("?authorId=" + authorId.ToString());
|
||||
return Get<List<BookResource>>(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -53,8 +53,8 @@ public abstract class IntegrationTestBase
|
|||
public ClientBase<RootFolderResource> RootFolders;
|
||||
public AuthorClient Author;
|
||||
public ClientBase<TagResource> Tags;
|
||||
public ClientBase<BookResource> WantedMissing;
|
||||
public ClientBase<BookResource> WantedCutoffUnmet;
|
||||
public WantedClient WantedMissing;
|
||||
public WantedClient WantedCutoffUnmet;
|
||||
|
||||
private List<SignalRMessage> _signalRReceived;
|
||||
|
||||
|
|
@ -118,8 +118,8 @@ protected virtual void InitRestClients()
|
|||
RootFolders = new ClientBase<RootFolderResource>(RestClient, ApiKey);
|
||||
Author = new AuthorClient(RestClient, ApiKey);
|
||||
Tags = new ClientBase<TagResource>(RestClient, ApiKey);
|
||||
WantedMissing = new ClientBase<BookResource>(RestClient, ApiKey, "wanted/missing");
|
||||
WantedCutoffUnmet = new ClientBase<BookResource>(RestClient, ApiKey, "wanted/cutoff");
|
||||
WantedMissing = new WantedClient(RestClient, ApiKey, "wanted/missing");
|
||||
WantedCutoffUnmet = new WantedClient(RestClient, ApiKey, "wanted/cutoff");
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
|
|
|
|||
|
|
@ -138,6 +138,17 @@ public List<BookResource> GetBooks([FromQuery]int? authorId,
|
|||
return MapToResource(_bookService.GetBooks(bookIds), false);
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}/overview")]
|
||||
public object Overview(int id)
|
||||
{
|
||||
var overview = _editionService.GetEditionsByBook(id).Single(x => x.Monitored).Overview;
|
||||
return new
|
||||
{
|
||||
id,
|
||||
overview
|
||||
};
|
||||
}
|
||||
|
||||
[RestPostById]
|
||||
public ActionResult<BookResource> AddBook(BookResource bookResource)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -72,13 +72,10 @@ public static BookResource ToResource(this Book model)
|
|||
AuthorTitle = authorTitle,
|
||||
SeriesTitle = seriesTitle,
|
||||
Disambiguation = selectedEdition?.Disambiguation,
|
||||
Overview = selectedEdition?.Overview,
|
||||
Images = selectedEdition?.Images ?? new List<MediaCover>(),
|
||||
Links = model.Links.Concat(selectedEdition?.Links ?? new List<Links>()).ToList(),
|
||||
Ratings = selectedEdition?.Ratings ?? new Ratings(),
|
||||
Added = model.Added,
|
||||
Author = model.Author?.Value.ToResource(),
|
||||
Editions = model.Editions?.Value.ToResource() ?? new List<EditionResource>()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ private static IEnumerable<SearchResource> MapToResource(IEnumerable<object> res
|
|||
{
|
||||
var book = (NzbDrone.Core.Books.Book)result;
|
||||
resource.Book = book.ToResource();
|
||||
resource.Book.Overview = book.Editions.Value.Single(x => x.Monitored).Overview;
|
||||
resource.Book.Author = book.Author.Value.ToResource();
|
||||
resource.Book.Editions = book.Editions.Value.ToResource();
|
||||
resource.ForeignId = book.ForeignBookId;
|
||||
|
||||
var cover = book.Editions.Value.Single(x => x.Monitored).Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
|
||||
|
|
|
|||
Loading…
Reference in a new issue