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

Tickets #153

Merged
merged 51 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
5758c47
Tickets init
dmirgaleev Jul 8, 2023
3bfe63d
WIP tickets
Jul 17, 2023
e488006
WIP tickets
Jul 17, 2023
58a01e9
WIP TicketUpdates and TicketTransfers
Jul 18, 2023
2c66f86
WIP TicketBalance Diagnostics
dmirgaleev Jul 28, 2023
7201bdd
WIP Diagnostics
dmirgaleev Jul 30, 2023
8f22fdd
Equal balance till level 45
dmirgaleev Jul 30, 2023
cf06a8d
Add cache for tickets
dmirgaleev Jul 31, 2023
69fd811
Balances are correct here
dmirgaleev Aug 1, 2023
efdc193
Test migration
dmirgaleev Aug 1, 2023
edf4c19
WIP Revert
dmirgaleev Aug 1, 2023
2b85b4d
Revert seems working
dmirgaleev Aug 2, 2023
494ffd3
revert test
dmirgaleev Aug 5, 2023
dc3cac6
revert seems fine
dmirgaleev Aug 5, 2023
6b0e2af
Clean up
dmirgaleev Aug 7, 2023
ba60f8b
Clean up
dmirgaleev Aug 7, 2023
57bcc24
Clean up
dmirgaleev Aug 8, 2023
d7ca9e9
Fixes
dmirgaleev Aug 9, 2023
55c99eb
parentId
dmirgaleev Aug 10, 2023
93f9ec0
WIP Transfers
dmirgaleev Aug 10, 2023
d1a371e
Move to Proto16
dmirgaleev Aug 10, 2023
7cd383c
Update SonarCloud
dmirgaleev Aug 11, 2023
f616a3d
Clean up
dmirgaleev Aug 11, 2023
596356d
Return nairobi migration
dmirgaleev Aug 11, 2023
13c4af5
Return tickets migration
dmirgaleev Aug 13, 2023
04e5075
Fix Ids
dmirgaleev Aug 14, 2023
61a3f0b
WIP Tickets API
dmirgaleev Aug 15, 2023
2779407
WIP API
dmirgaleev Aug 15, 2023
6564cd9
WIP Filters
dmirgaleev Aug 18, 2023
9957daf
Fix
dmirgaleev Aug 19, 2023
752c235
Fix
dmirgaleev Aug 19, 2023
4cf4671
Fix
dmirgaleev Aug 20, 2023
e4eb5b2
Clean up
dmirgaleev Aug 20, 2023
ca37de7
Fixes
dmirgaleev Aug 22, 2023
e69eba7
Fix processors
dmirgaleev Aug 22, 2023
7b9e620
Ticket Processors fix
dmirgaleev Aug 23, 2023
b642c79
Remove metadata
dmirgaleev Aug 23, 2023
56b2eb3
String to JsonElement for GetTicketBalance
dmirgaleev Aug 23, 2023
cba8bc0
Rename `Ticket.ContentType*` to `Ticket.Type*`
Groxan Aug 25, 2023
f56c32f
Remove `Ticket.JsonType`, add more indexes
Groxan Aug 25, 2023
f0964e4
Fix models
Groxan Aug 25, 2023
cd770c7
Improve tickets cache
Groxan Aug 28, 2023
818afd0
Fix tickets rpc
Groxan Aug 28, 2023
dfd416e
Minor fixes
Groxan Aug 28, 2023
b71aec7
Refactoring and fixes
Groxan Aug 30, 2023
3040fa4
Better handle multiple transfers in a single op
Groxan Aug 30, 2023
314d2b1
More bug-resistant ticket updates parsing
Groxan Aug 30, 2023
c438649
Minor refactoring
Groxan Aug 31, 2023
bced25d
API refactoring
Groxan Aug 31, 2023
344ff72
More strict ticket transfers recognition
Groxan Aug 31, 2023
4b7e5d9
Add MichelineParameter, improve docs
Groxan Sep 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,23 @@ jobs:
runs-on: windows-latest
if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }}
steps:
- name: Set up JDK 11
uses: actions/setup-java@v1
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: 1.11
- uses: actions/checkout@v2
java-version: 17
distribution: 'zulu' # Alternative distribution options are available.
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Cache SonarCloud packages
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ~\sonar\cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache SonarCloud scanner
id: cache-sonar-scanner
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: .\.sonar\scanner
key: ${{ runner.os }}-sonar-scanner
Expand All @@ -44,6 +45,6 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
shell: powershell
run: |
.\.sonar\scanner\dotnet-sonarscanner begin /k:"baking-bad_tzkt" /o:"baking-bad" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io"
.\.sonar\scanner\dotnet-sonarscanner begin /k:"baking-bad_tzkt" /o:"baking-bad" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io"
dotnet build
.\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
.\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,7 @@ mumbai-stop:
docker-compose -f docker-compose.mumbai.yml down

mumbai-db-start:
docker-compose -f docker-compose.mumbai.yml up -d mumbai-db
reset:
docker-compose -f docker-compose.mumbai.yml down --volumes
docker-compose -f docker-compose.mumbai.yml up -d mumbai-db
309 changes: 309 additions & 0 deletions Tzkt.Api/Controllers/TicketsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
using Microsoft.AspNetCore.Mvc;
using Tzkt.Api.Models;
using Tzkt.Api.Repositories;
using Tzkt.Api.Services;
using Tzkt.Api.Services.Cache;

namespace Tzkt.Api.Controllers
{
[ApiController]
[Route("v1/tickets")]
public class TicketsController : ControllerBase
{
readonly TicketsRepository Tickets;
readonly StateCache State;
readonly ResponseCacheService ResponseCache;

public TicketsController(TicketsRepository tickets, StateCache state, ResponseCacheService responseCache)
{
Tickets = tickets;
State = state;
ResponseCache = responseCache;
}

#region tickets
/// <summary>
/// Get tickets count
/// </summary>
/// <remarks>
/// Returns a total number of tickets.
/// </remarks>
/// <param name="filter">Filter</param>
/// <returns></returns>
[HttpGet("count")]
public async Task<ActionResult<int>> GetTicketsCount([FromQuery] TicketFilter filter)
{
if (filter.Empty)
return Ok(State.Current.TicketsCount);

var query = ResponseCacheService.BuildKey(Request.Path.Value, ("filter", filter));

if (ResponseCache.TryGet(query, out var cached))
return this.Bytes(cached);

var res = await Tickets.GetTicketsCount(filter);
cached = ResponseCache.Set(query, res);
return this.Bytes(cached);
}

/// <summary>
/// Get tickets
/// </summary>
/// <remarks>
/// Returns a list of tickets.
/// </remarks>
/// <param name="filter">Filter</param>
/// <param name="pagination">Pagination</param>
/// <param name="selection">Selection</param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult<IEnumerable<Ticket>>> GetTickets(
[FromQuery] TicketFilter filter,
[FromQuery] Pagination pagination,
[FromQuery] Selection selection)
{
var query = ResponseCacheService.BuildKey(Request.Path.Value,
("filter", filter), ("pagination", pagination), ("selection", selection));

if (ResponseCache.TryGet(query, out var cached))
return this.Bytes(cached);

object res;
if (selection.select == null)
{
res = await Tickets.GetTickets(filter, pagination);
}
else
{
res = new SelectionResponse
{
Cols = selection.select.Fields?.Select(x => x.Alias).ToArray(),
Rows = await Tickets.GetTickets(filter, pagination, selection.select.Fields ?? selection.select.Values)
};
}
cached = ResponseCache.Set(query, res);
return this.Bytes(cached);
}
#endregion

#region ticket balances
/// <summary>
/// Get ticket balances count
/// </summary>
/// <remarks>
/// Returns a total number of ticket balances.
/// </remarks>
/// <param name="filter">Filter</param>
/// <returns></returns>
[HttpGet("balances/count")]
public async Task<ActionResult<int>> GetTicketBalancesCount([FromQuery] TicketBalanceFilter filter)
{
if (filter.Empty)
return Ok(State.Current.TicketBalancesCount);

#region optimizations
if (filter.account != null && (filter.account.Eq == -1 || filter.account.In?.Count == 0 && !filter.account.InHasNull))
return Ok(0);
#endregion

var query = ResponseCacheService.BuildKey(Request.Path.Value, ("filter", filter));

if (ResponseCache.TryGet(query, out var cached))
return this.Bytes(cached);

var res = await Tickets.GetTicketBalancesCount(filter);
cached = ResponseCache.Set(query, res);
return this.Bytes(cached);
}

/// <summary>
/// Get ticket balances
/// </summary>
/// <remarks>
/// Returns a list of ticket balances.
/// </remarks>
/// <param name="filter">Filter</param>
/// <param name="pagination">Pagination</param>
/// <param name="selection">Selection</param>
/// <returns></returns>
[HttpGet("balances")]
public async Task<ActionResult<IEnumerable<TicketBalance>>> GetTicketBalances(
[FromQuery] TicketBalanceFilter filter,
[FromQuery] Pagination pagination,
[FromQuery] Selection selection)
{
#region optimizations
if (filter.account != null && (filter.account.Eq == -1 || filter.account.In?.Count == 0 && !filter.account.InHasNull))
return Ok(Enumerable.Empty<TicketBalance>());
#endregion

var query = ResponseCacheService.BuildKey(Request.Path.Value,
("filter", filter), ("pagination", pagination), ("selection", selection));

if (ResponseCache.TryGet(query, out var cached))
return this.Bytes(cached);

object res;
if (selection.select == null)
{
res = await Tickets.GetTicketBalances(filter, pagination);
}
else
{
res = new SelectionResponse
{
Cols = selection.select.Fields?.Select(x => x.Alias).ToArray(),
Rows = await Tickets.GetTicketBalances(filter, pagination, selection.select.Fields ?? selection.select.Values)
};
}
cached = ResponseCache.Set(query, res);
return this.Bytes(cached);
}
#endregion

#region ticket transfers
/// <summary>
/// Get ticket transfers count
/// </summary>
/// <remarks>
/// Returns the total number of ticket transfers.
/// </remarks>
/// <param name="filter">Filter</param>
/// <returns></returns>
[HttpGet("transfers/count")]
public async Task<ActionResult<int>> GetTicketTransfersCount([FromQuery] TicketTransferFilter filter)
{
if (filter.Empty)
return Ok(State.Current.TicketTransfersCount);

#region optimizations
if (filter.from != null && (filter.from.Eq == -1 || filter.from.In?.Count == 0 && !filter.from.InHasNull))
return Ok(0);

if (filter.to != null && (filter.to.Eq == -1 || filter.to.In?.Count == 0 && !filter.to.InHasNull))
return Ok(0);

if (filter.anyof != null && (filter.anyof.Eq == -1 || filter.anyof.In?.Count == 0 && !filter.anyof.InHasNull))
return Ok(0);
#endregion

var query = ResponseCacheService.BuildKey(Request.Path.Value, ("filter", filter));

if (ResponseCache.TryGet(query, out var cached))
return this.Bytes(cached);

var res = await Tickets.GetTicketTransfersCount(filter);
cached = ResponseCache.Set(query, res);
return this.Bytes(cached);
}

/// <summary>
/// Get ticket transfers
/// </summary>
/// <remarks>
/// Returns a list of ticket transfers.
/// </remarks>
/// <param name="filter">Filter</param>
/// <param name="pagination">Pagination</param>
/// <param name="selection">Selection</param>
/// <returns></returns>
[HttpGet("transfers")]
public async Task<ActionResult<IEnumerable<TicketTransfer>>> GetTicketTransfers(
[FromQuery] TicketTransferFilter filter,
[FromQuery] Pagination pagination,
[FromQuery] Selection selection)
{
#region optimizations
if (filter.from != null && (filter.from.Eq == -1 || filter.from.In?.Count == 0 && !filter.from.InHasNull))
return Ok(Enumerable.Empty<TicketTransfer>());

if (filter.to != null && (filter.to.Eq == -1 || filter.to.In?.Count == 0 && !filter.to.InHasNull))
return Ok(Enumerable.Empty<TicketTransfer>());

if (filter.anyof != null && (filter.anyof.Eq == -1 || filter.anyof.In?.Count == 0 && !filter.anyof.InHasNull))
return Ok(Enumerable.Empty<TicketTransfer>());
#endregion

var query = ResponseCacheService.BuildKey(Request.Path.Value,
("filter", filter), ("pagination", pagination), ("selection", selection));

if (ResponseCache.TryGet(query, out var cached))
return this.Bytes(cached);

object res;
if (selection.select == null)
{
res = await Tickets.GetTicketTransfers(filter, pagination);
}
else
{
res = new SelectionResponse
{
Cols = selection.select.Fields?.Select(x => x.Alias).ToArray(),
Rows = await Tickets.GetTicketTransfers(filter, pagination, selection.select.Fields ?? selection.select.Values)
};
}
cached = ResponseCache.Set(query, res);
return this.Bytes(cached);
}
#endregion

#region historical balances
/// <summary>
/// Get historical ticket balances
/// </summary>
/// <remarks>
/// Returns a list of ticket balances at the end of the specified block.
/// Note, this endpoint is quite heavy, therefore at least one of the filters
/// (`account`, `ticket.id`, `ticket.ticketer`) must be specified.
/// </remarks>
/// <param name="level">Level of the block at the end of which historical balances must be calculated</param>
/// <param name="filter">Filter</param>
/// <param name="pagination">Pagination</param>
/// <param name="selection">Selection</param>
/// <returns></returns>
[HttpGet("historical_balances/{level:int}")]
public async Task<ActionResult<IEnumerable<TicketBalanceShort>>> GetHistoricalTicketBalances(int level,
[FromQuery] TicketBalanceShortFilter filter,
[FromQuery] Pagination pagination,
[FromQuery] Selection selection)
{

if (filter.account?.Eq == null &&
filter.account?.In == null &&
filter.ticket.id?.Eq == null &&
filter.ticket.id?.In == null &&
filter.ticket.ticketer?.Eq == null &&
filter.ticket.ticketer?.In == null)
return new BadRequest("query", "At least one of the filters (`account`, `ticket.id`, `ticket.ticketer`) must be specified");

#region optimizations
if (filter.account != null && (filter.account.Eq == -1 || filter.account.In?.Count == 0 && !filter.account.InHasNull))
return Ok(Enumerable.Empty<TicketBalanceShort>());
#endregion

var query = ResponseCacheService.BuildKey(Request.Path.Value,
("filter", filter), ("pagination", pagination), ("selection", selection));

if (ResponseCache.TryGet(query, out var cached))
return this.Bytes(cached);

object res;
if (selection.select == null)
{
res = await Tickets.GetHistoricalTicketBalances(level, filter, pagination);
}
else
{
res = new SelectionResponse
{
Cols = selection.select.Fields?.Select(x => x.Alias).ToArray(),
Rows = await Tickets.GetHistoricalTicketBalances(level, filter, pagination, selection.select.Fields ?? selection.select.Values)
};
}
cached = ResponseCache.Set(query, res);
return this.Bytes(cached);
}
#endregion
}
}
Loading
Loading