Skip to content

Commit

Permalink
add new API endpoints for file manager/consume page
Browse files Browse the repository at this point in the history
This rethinks the way the requests are handled in an attempt to simplify usage and optimize response
sizes. There are 7 new endpoints to replace existing ones:
- `/bibles/language/LANGUAGE_ID` returns basic details about the Bible versions for a language
- `/bibles/BIBLE_ID/book/BOOK_ID` returns info about a given book of a given Bible version,
    including URLs for downloading
- `/passages/language/LANGUAGE_ID/resource/TYPE` returns basic details for all passages tied to the
    given resource type with content in the given language
- `/passages/PASSAGE_ID/language/LANGUAGE_ID` returns content ids for all resources tied to the
    given passage whether through verse or overlapping passage, in the given language. Also includes
    supporting resources for the given passage.
- `/resources/language/LANGUAGE_ID/book/BOOK_ID?resourceType=CBBTER&resourceType=TyndaleBibleDictionary`
    returns all the content ids and basic info for a given book and language, filtered to the
    resources you specified. Groups by chapter.
- `/resources/content/CONTENT_ID` returns the JSON content for text types or redirects to the CDN
    for other types
- `/resources/metadata/CONTENT_ID` returns the display name and any other relevant metadata for
    content
  • Loading branch information
Kyle Grinstead committed Sep 28, 2023
1 parent 473b3b9 commit f718a0c
Show file tree
Hide file tree
Showing 16 changed files with 493 additions and 273 deletions.
25 changes: 17 additions & 8 deletions src/Aquifer.API/Modules/Bibles/BibleResponse.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
namespace Aquifer.API.Modules.Bibles;
using Aquifer.API.Utilities;

public class BibleBookResponse
namespace Aquifer.API.Modules.Bibles;

public class BibleResponse
{
public int LanguageId { get; set; }
public int Id { get; set; }
public string Name { get; set; } = null!;
public string Abbreviation { get; set; } = null!;

public IEnumerable<BibleBookResponseContent> Contents { get; set; } =
new List<BibleBookResponseContent>();
public IEnumerable<BibleResponseBook> Books { get; set; } =
new List<BibleResponseBook>();
}

public class BibleBookResponseContent
public class BibleResponseBook
{
public int BookId { get; set; }
public BibleUtilities.BookCode BookCode { get; set; }
public string DisplayName { get; set; } = null!;
public string TextUrl { get; set; } = null!;
public object? AudioUrls { get; set; }
public int TextSize { get; set; }
public int AudioSize { get; set; }
public int ChapterCount { get; set; }
}

public class BibleBookDetailsResponse : BibleResponseBook
{
public string TextUrl { get; set; } = null!;
public object? AudioUrls { get; set; }
}
60 changes: 46 additions & 14 deletions src/Aquifer.API/Modules/Bibles/BiblesModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,63 @@ public class BiblesModule : IModule
public IEndpointRouteBuilder MapEndpoints(IEndpointRouteBuilder endpoints)
{
var group = endpoints.MapGroup("bibles");
group.MapGet("language/{languageId:int}", GetBibleContentsByLanguage);
group.MapGet("language/{languageId:int}", GetBibleByLanguage);
group.MapGet("{bibleId:int}/book/{bookId:int}", GetBibleBookDetails);

return endpoints;
}

private async Task<Ok<List<BibleBookResponse>>> GetBibleContentsByLanguage(int languageId,
private async Task<Ok<List<BibleResponse>>> GetBibleByLanguage(int languageId,
AquiferDbContext dbContext,
CancellationToken cancellationToken)
{
var bibles = await dbContext.Bibles.Where(x => x.LanguageId == languageId)
.Select(x => new BibleBookResponse
.Select(bible => new BibleResponse
{
LanguageId = x.LanguageId,
Name = x.Name,
Contents = x.BibleBookContents.Select(y => new BibleBookResponseContent
{
BookId = y.BookId,
DisplayName = y.DisplayName,
TextUrl = y.TextUrl,
TextSize = y.TextSize,
AudioUrls = JsonUtilities.DefaultSerialize(y.AudioUrls),
AudioSize = y.AudioSize
})
Name = bible.Name,
Abbreviation = bible.Abbreviation,
Id = bible.Id,
Books = bible.BibleBookContents.OrderBy(book => book.BookId).Select(book =>
new BibleResponseBook
{
BookId = book.BookId,
BookCode = BibleUtilities.BookIdToCode(book.BookId),
DisplayName = book.DisplayName,
TextSize = book.TextSize,
AudioSize = book.AudioSize,
ChapterCount = book.ChapterCount
})
}).ToListAsync(cancellationToken);

return TypedResults.Ok(bibles);
}

private async Task<Results<Ok<BibleBookDetailsResponse>, NotFound>> GetBibleBookDetails(
int bibleId,
int bookId,
AquiferDbContext dbContext,
CancellationToken cancellationToken)
{
var book = await dbContext.BibleBookContents
.SingleOrDefaultAsync(b => b.BibleId == bibleId && b.BookId == bookId, cancellationToken);

if (book == null)
{
return TypedResults.NotFound();
}

var response = new BibleBookDetailsResponse
{
AudioSize = book.AudioSize,
AudioUrls = book.AudioUrls,
BookId = book.BookId,
BookCode = BibleUtilities.BookIdToCode(book.BookId),
ChapterCount = book.ChapterCount,
DisplayName = book.DisplayName,
TextSize = book.TextSize,
TextUrl = book.TextUrl
};

return TypedResults.Ok(response);
}
}
43 changes: 0 additions & 43 deletions src/Aquifer.API/Modules/Passages/PassageResourcesResponse.cs

This file was deleted.

14 changes: 0 additions & 14 deletions src/Aquifer.API/Modules/Passages/PassageResponse.cs

This file was deleted.

135 changes: 84 additions & 51 deletions src/Aquifer.API/Modules/Passages/PassagesModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,64 +11,97 @@ public class PassagesModule : IModule
public IEndpointRouteBuilder MapEndpoints(IEndpointRouteBuilder endpoints)
{
var group = endpoints.MapGroup("passages");
group.MapGet("/resources/language/{languageId:int}", GetPassageResourcesByLanguage);
group.MapGet("language/{languageId:int}/resource/{resourceType}", GetPassagesByLanguageAndResource);
group.MapGet("{passageId:int}/language/{languageId:int}", GetPassageDetailsForLanguage);
return endpoints;
}

public async Task<Ok<List<PassageResourcesResponse>>> GetPassageResourcesByLanguage(int languageId,
private async Task<Ok<List<PassagesBookResponse>>> GetPassagesByLanguageAndResource(int languageId,
ResourceEntityType resourceType,
AquiferDbContext dbContext,
CancellationToken cancellationToken)
{
var passageContent = (await dbContext.Passages.Select(passage =>
new PassageResourcesResponse
var passagesByBook = (await dbContext.Passages
.Where(p => p.PassageResources.Any(pr =>
pr.Resource.Type == resourceType &&
pr.Resource.ResourceContents.Any(rc => rc.LanguageId == languageId)))
.Select(passage =>
new PassagesResponsePassage
{
Id = passage.Id,
PassageStartDetails = BibleUtilities.TranslateVerseId(passage.StartVerseId),
PassageEndDetails = BibleUtilities.TranslateVerseId(passage.EndVerseId)
}
).ToListAsync(cancellationToken))
.GroupBy(passage => passage.BookId)
.Select(grouped => new PassagesBookResponse
{
BookId = grouped.Key, Passages = grouped.OrderBy(p => p.StartChapter).ThenBy(p => p.StartVerse)
})
.OrderBy(book => book.BookId).ToList();

return TypedResults.Ok(passagesByBook);
}

private async Task<Results<Ok<PassageDetailsResponse>, NotFound>> GetPassageDetailsForLanguage(
int passageId,
int languageId,
AquiferDbContext dbContext,
CancellationToken cancellationToken)
{
var passage = await dbContext.Passages.FindAsync(new object?[] { passageId }, cancellationToken);
if (passage == null)
{
return TypedResults.NotFound();
}

var passageResourceContent = await dbContext.PassageResources
// find all passages that overlap with the current passage
.Where(pr => (pr.Passage.StartVerseId >= passage.StartVerseId &&
pr.Passage.StartVerseId <= passage.EndVerseId) ||
(pr.Passage.EndVerseId >= passage.StartVerseId &&
pr.Passage.EndVerseId <= passage.EndVerseId))
.SelectMany(pr => pr.Resource.ResourceContents.Where(rc => rc.LanguageId == languageId).Select(rc =>
new PassageDetailsResponseContent
{
PassageStartDetails = BibleUtilities.TranslateVerseId(passage.StartVerseId),
PassageEndDetails = BibleUtilities.TranslateVerseId(passage.EndVerseId),
Resources = passage.PassageResources
.Where(pr => pr.Resource.Type != ResourceEntityType.TyndaleBibleDictionary)
.Select(passageResource =>
new PassageResourcesResponseResource
{
Type = (int)passageResource.Resource.Type,
MediaType = (int)passageResource.Resource.MediaType,
EnglishLabel = passageResource.Resource.EnglishLabel,
Tag = passageResource.Resource.Tag,
Content =
passageResource.Resource.ResourceContents
.Select(resourceContent =>
new PassageResourcesResponseResourceContent
{
LanguageId = resourceContent.LanguageId,
DisplayName = resourceContent.DisplayName,
Summary = resourceContent.Summary,
Content = JsonUtilities.DefaultSerialize(resourceContent.Content),
ContentSize = resourceContent.ContentSize
}).FirstOrDefault(content => content.LanguageId == languageId),
SupportingResources = passageResource.Resource.SupportingResources
.Where(sr => sr.Type != ResourceEntityType.TyndaleBibleDictionary)
.Select(
supportingResource => new PassageResourcesResponseResource
{
Type = (int)supportingResource.Type,
MediaType = (int)supportingResource.MediaType,
EnglishLabel = supportingResource.EnglishLabel,
Tag = supportingResource.Tag,
Content = supportingResource.ResourceContents.Select(resourceContent =>
new PassageResourcesResponseResourceContent
{
LanguageId = resourceContent.LanguageId,
DisplayName = resourceContent.DisplayName,
Summary = resourceContent.Summary,
Content =
JsonUtilities.DefaultSerialize(resourceContent.Content),
ContentSize = resourceContent.ContentSize
}).FirstOrDefault(content => content.LanguageId == languageId)
})
})
}).ToListAsync(cancellationToken))
.OrderBy(passage => passage.BookId)
.ThenBy(passage => passage.StartChapter).ThenBy(passage => passage.StartVerse).ToList();
ContentId = rc.Id,
ContentSize = rc.ContentSize,
MediaTypeName = rc.MediaType,
TypeName = pr.Resource.Type
}))
.ToListAsync(cancellationToken);

var verseResourceContent = await dbContext.VerseResources
// find all verses contained within the current passage
.Where(vr => vr.VerseId >= passage.StartVerseId && vr.VerseId <= passage.EndVerseId)
.SelectMany(vr => vr.Resource.ResourceContents.Where(rc => rc.LanguageId == languageId).Select(rc =>
new PassageDetailsResponseContent
{
ContentId = rc.Id,
ContentSize = rc.ContentSize,
MediaTypeName = rc.MediaType,
TypeName = vr.Resource.Type
}))
.ToListAsync(cancellationToken);

var supportingResourceContent = await dbContext.PassageResources.Where(pr => pr.PassageId == passageId)
.SelectMany(pr => pr.Resource.SupportingResources
.SelectMany(sr => sr.ResourceContents.Where(rc => rc.LanguageId == languageId)
.Select(rc => new PassageDetailsResponseContent
{
ContentId = rc.Id,
ContentSize = rc.ContentSize,
MediaTypeName = rc.MediaType,
TypeName = sr.Type
})))
.ToListAsync(cancellationToken);

return TypedResults.Ok(passageContent);
return TypedResults.Ok(new PassageDetailsResponse
{
Id = passage.Id,
PassageStartDetails = BibleUtilities.TranslateVerseId(passage.StartVerseId),
PassageEndDetails = BibleUtilities.TranslateVerseId(passage.EndVerseId),
Contents = passageResourceContent.Concat(verseResourceContent).Concat(supportingResourceContent)
});
}
}
39 changes: 39 additions & 0 deletions src/Aquifer.API/Modules/Passages/PassagesResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Aquifer.Data.Entities;
using System.Text.Json.Serialization;

namespace Aquifer.API.Modules.Passages;

public class PassagesBookResponse
{
public int BookId { get; set; }
public IEnumerable<PassagesResponsePassage> Passages { get; set; } = null!;
}

public class PassagesResponsePassage
{
public int Id { get; set; }
public int BookId => PassageStartDetails.BookId;
public int StartChapter => PassageStartDetails.Chapter;
public int EndChapter => PassageEndDetails.Chapter;
public int StartVerse => PassageStartDetails.Verse;
public int EndVerse => PassageEndDetails.Verse;

[JsonIgnore]
public (int BookId, int Chapter, int Verse) PassageStartDetails { get; set; }

[JsonIgnore]
public (int BookId, int Chapter, int Verse) PassageEndDetails { get; set; }
}

public class PassageDetailsResponse : PassagesResponsePassage
{
public IEnumerable<PassageDetailsResponseContent> Contents { get; set; } = null!;
}

public class PassageDetailsResponseContent
{
public int ContentId { get; set; }
public ResourceEntityType TypeName { get; set; }
public ResourceContentMediaType MediaTypeName { get; set; }
public int ContentSize { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Aquifer.Data.Entities;

namespace Aquifer.API.Modules.Resources;

public class ResourceContentInfoForBookResponse
{
public IEnumerable<ResourceContentInfoForChapter> Chapters { get; set; } = null!;
}

public class ResourceContentInfoForChapter
{
public int ChapterNumber { get; set; }
public IEnumerable<ResourceContentInfo> Contents { get; set; } = null!;
}

public class ResourceContentInfo
{
public int ContentId { get; set; }
public int ContentSize { get; set; }
public ResourceContentMediaType MediaType { get; set; }
public ResourceEntityType Type { get; set; }
}
Loading

0 comments on commit f718a0c

Please sign in to comment.