• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

karanshukla / openresto / 27474227282

13 Jun 2026 05:42PM UTC coverage: 82.784% (-0.7%) from 83.466%
27474227282

push

github

karanshukla
fix notifications

3060 of 4177 branches covered (73.26%)

Branch coverage included in aggregate %.

495 of 665 new or added lines in 9 files covered. (74.44%)

3 existing lines in 3 files now uncovered.

8197 of 9421 relevant lines covered (87.01%)

59.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/OpenRestoApi/Controllers/NotificationsController.cs
1
using Microsoft.AspNetCore.Authorization;
2
using Microsoft.AspNetCore.Mvc;
3
using OpenRestoApi.Core.Application.DTOs;
4
using OpenRestoApi.Core.Application.Interfaces;
5

6
namespace OpenRestoApi.Controllers;
7

8
[ApiController]
9
[Route("api/admin")]
10
[Authorize]
11
public class NotificationsController(INotificationService notificationService) : ControllerBase
×
12
{
13
    private readonly INotificationService _notifications = notificationService;
×
14

15
    /// <summary>
16
    /// List notification history with optional filters, newest first.
17
    /// GET /api/admin/notifications?restaurantId=1&amp;type=BookingCreated&amp;unreadOnly=true&amp;page=1&amp;pageSize=20
18
    /// restaurantId is optional — omit to see all restaurants.
19
    /// type values: BookingCreated | BookingCancelled | RestaurantNearlyFull
20
    /// </summary>
21
    [HttpGet("notifications")]
22
    public async Task<IActionResult> GetNotifications(
23
        [FromQuery] int? restaurantId,
24
        [FromQuery] string? type,
25
        [FromQuery] bool? unreadOnly,
26
        [FromQuery] int page = 1,
27
        [FromQuery] int pageSize = 20)
28
    {
29
        pageSize = Math.Clamp(pageSize, 1, 100);
×
30
        page = Math.Max(1, page);
×
31

32
        (List<AdminNotificationDto> items, int total) = await _notifications.GetNotificationsAsync(
×
33
            restaurantId, type, unreadOnly, page, pageSize);
×
34

NEW
35
        return Ok(new { items, totalCount = total, page, pageSize });
×
36
    }
×
37

38
    /// <summary>
39
    /// Unread count badge. Omit restaurantId to get total across all restaurants.
40
    /// GET /api/admin/notifications/unread-count
41
    /// GET /api/admin/notifications/unread-count?restaurantId=1
42
    /// </summary>
43
    [HttpGet("notifications/unread-count")]
44
    public async Task<IActionResult> GetUnreadCount([FromQuery] int? restaurantId)
45
    {
46
        int count = await _notifications.GetUnreadCountAsync(restaurantId);
×
47
        return Ok(new { count });
×
48
    }
×
49

50
    /// <summary>
51
    /// Mark a single notification as read.
52
    /// PATCH /api/admin/notifications/{id}/read
53
    /// </summary>
54
    [HttpPatch("notifications/{id:int}/read")]
55
    public async Task<IActionResult> MarkRead(int id)
56
    {
57
        await _notifications.MarkReadAsync(id);
×
58
        return NoContent();
×
59
    }
×
60

61
    /// <summary>
62
    /// Mark all notifications for a restaurant as read.
63
    /// PATCH /api/admin/notifications/read-all?restaurantId=1
64
    /// </summary>
65
    [HttpPatch("notifications/read-all")]
66
    public async Task<IActionResult> MarkAllRead([FromQuery] int restaurantId)
67
    {
68
        if (restaurantId <= 0)
×
69
            return BadRequest(new { error = "restaurantId is required." });
×
70

71
        await _notifications.MarkAllReadAsync(restaurantId);
×
72
        return NoContent();
×
73
    }
×
74

75
    /// <summary>
76
    /// Returns the VAPID public key for the frontend to use when subscribing.
77
    /// GET /api/admin/push/vapid-public-key
78
    /// Returns 204 if VAPID is not configured (push disabled).
79
    /// </summary>
80
    [HttpGet("push/vapid-public-key")]
81
    public IActionResult GetVapidPublicKey()
82
    {
83
        string? key = _notifications.GetVapidPublicKey();
×
84
        if (key is null) return NoContent();
×
85
        return Ok(new { publicKey = key });
×
86
    }
87

88
    /// <summary>
89
    /// Register a browser push subscription.
90
    /// POST /api/admin/push/subscribe?restaurantId=1
91
    /// Body: { endpoint, p256dh, auth, userAgent? }
92
    /// </summary>
93
    [HttpPost("push/subscribe")]
94
    public async Task<IActionResult> Subscribe(
95
        [FromQuery] int restaurantId,
96
        [FromBody] PushSubscribeRequest request)
97
    {
98
        if (restaurantId <= 0)
×
99
            return BadRequest(new { error = "restaurantId is required." });
×
100

101
        await _notifications.SubscribeAsync(restaurantId, request);
×
102
        return Ok();
×
103
    }
×
104

105
    /// <summary>
106
    /// Remove a push subscription (logout / permission revoked).
107
    /// DELETE /api/admin/push/subscribe
108
    /// Body: "https://fcm.googleapis.com/..."
109
    /// </summary>
110
    [HttpDelete("push/subscribe")]
111
    public async Task<IActionResult> Unsubscribe([FromBody] string endpoint)
112
    {
113
        await _notifications.UnsubscribeAsync(endpoint);
×
114
        return NoContent();
×
115
    }
×
116

117
    [HttpDelete("notifications/all")]
118
    public async Task<IActionResult> DeleteAll(
119
        [FromQuery] int? restaurantId,
120
        [FromQuery] string? type,
121
        [FromQuery] bool? unreadOnly)
122
    {
NEW
123
        await _notifications.DeleteAllAsync(restaurantId, type, unreadOnly);
×
NEW
124
        return NoContent();
×
NEW
125
    }
×
126

127
    [HttpDelete("notifications/{id:int}")]
128
    public async Task<IActionResult> DeleteNotification(int id)
129
    {
NEW
130
        await _notifications.DeleteByIdAsync(id);
×
NEW
131
        return NoContent();
×
NEW
132
    }
×
133

134
    [HttpDelete("notifications")]
135
    public async Task<IActionResult> DeleteNotifications([FromBody] List<int> ids)
136
    {
NEW
137
        if (ids == null || ids.Count == 0)
×
NEW
138
            return BadRequest(new { error = "List of notification IDs is required." });
×
139

NEW
140
        await _notifications.DeleteByIdsAsync(ids);
×
NEW
141
        return NoContent();
×
NEW
142
    }
×
143
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc