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

Add basic IFTTT API support #37

Merged
merged 3 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 23 additions & 11 deletions OrcanodeMonitor/Api/NodeStateEventsController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using OrcanodeMonitor.Core;
using OrcanodeMonitor.Data;
using OrcanodeMonitor.Models;
using System.Dynamic;
Expand All @@ -12,6 +13,7 @@ namespace OrcanodeMonitor.Api
[ApiController]
public class NodeStateEventsController : ControllerBase
{
const int _defaultLimit = 50;
private readonly OrcanodeMonitorContext _databaseContext;

public NodeStateEventsController(OrcanodeMonitorContext context)
Expand Down Expand Up @@ -41,28 +43,38 @@ private JsonResult GetEvents(int limit)
return new JsonResult(jsonElement);
}

// GET is not used by IFTTT so this does not require a service key.
// GET: api/ifttt/v1/triggers/<TestController>
[HttpGet]
public JsonResult Get()
public IActionResult Get()
{
return GetEvents(50);
return GetEvents(_defaultLimit);
}

// POST api/ifttt/v1/triggers/<TestController>
[HttpPost]
public IActionResult Post([FromBody] string value)
public IActionResult Post([FromBody] JsonElement requestBody)
{
var failure = Fetcher.CheckIftttServiceKey(Request);
if (failure != null)
{
return Unauthorized(failure);
}

if (requestBody.ValueKind != JsonValueKind.Object)
{
return BadRequest("Invalid JSON data.");
}

try
{
dynamic requestBody = JsonSerializer.Deserialize<ExpandoObject>(value);
if (!requestBody.TryGetProperty("limit", out JsonElement limitElement))
{
return BadRequest("Invalid JSON data.");
}
int limit = 50;
if (limitElement.TryGetInt32(out int explicitLimit))
int limit = _defaultLimit;
if (requestBody.TryGetProperty("limit", out JsonElement limitElement))
{
limit = explicitLimit;
if (limitElement.TryGetInt32(out int explicitLimit))
{
limit = explicitLimit;
}
}
if (requestBody.TryGetProperty("triggerFields", out JsonElement triggerFields))
{
Expand Down
223 changes: 223 additions & 0 deletions OrcanodeMonitor/Api/OrcanodesController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using OrcanodeMonitor.Core;
using OrcanodeMonitor.Data;
using OrcanodeMonitor.Models;

namespace OrcanodeMonitor.Api
{
public class ErrorResponse
{
public List<ErrorItem> Errors { get; set; }
}

public class ErrorItem
{
public string Message { get; set; }
}

[Route("api/ifttt/v1/queries/[controller]")]
[ApiController]
public class OrcanodesController : ControllerBase
{
const int _defaultLimit = 50;
private readonly OrcanodeMonitorContext _databaseContext;

public OrcanodesController(OrcanodeMonitorContext context)
{
_databaseContext = context;
}

// GET is not used by IFTTT so this does not require a service key.
// GET: api/ifttt/v1/queries/Orcanodes
[HttpGet]
public async Task<ActionResult<IEnumerable<Orcanode>>> GetOrcanode()
{
return await GetJsonNodesAsync(string.Empty, _defaultLimit);
}

#if false
// GET: api/ifttt/v1/queries/orcanodes/5
[HttpGet("{id}")]
public async Task<ActionResult<Orcanode>> GetOrcanode(int id)
{
var orcanode = await _databaseContext.Orcanodes.FindAsync(id);

if (orcanode == null)
{
return NotFound();
}

return orcanode;
}
#endif

private async Task<JsonResult> GetJsonNodesAsync(string cursor, int limit)
{
var nodes = await _databaseContext.Orcanodes.ToListAsync();

// Convert to IFTTT data transfer objects.
var jsonNodes = new List<OrcanodeIftttDTO>();
string myCursor = string.Empty;
foreach (Orcanode node in nodes)
{
string nodeCursor = node.ID.ToString();
if (cursor != string.Empty)
{
if (cursor != nodeCursor)
{
continue;
}
// Found the node with the cursor so we can continue normally now.
cursor = string.Empty;
}
if (jsonNodes.Count >= limit)
{
myCursor = node.ID.ToString();
break;
}
jsonNodes.Add(node.ToIftttDTO());
}

if (!myCursor.IsNullOrEmpty())
{
var dataResult = new { data = jsonNodes, cursor = myCursor };
var jsonString = JsonSerializer.Serialize(dataResult);
var jsonDocument = JsonDocument.Parse(jsonString);
var jsonElement = jsonDocument.RootElement;
return new JsonResult(jsonElement);
}
else
{
var dataResult = new { data = jsonNodes };
var jsonString = JsonSerializer.Serialize(dataResult);
var jsonDocument = JsonDocument.Parse(jsonString);
var jsonElement = jsonDocument.RootElement;
return new JsonResult(jsonElement);
}
}

// POST: api/ifttt/v1/queries/orcanodes
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async Task<ActionResult<Orcanode>> PostOrcanode([FromBody] JsonElement requestBody)
{
var failure = Fetcher.CheckIftttServiceKey(Request);
if (failure != null)
{
return Unauthorized(failure);
}

try
{
int limit = _defaultLimit;
if (requestBody.TryGetProperty("limit", out JsonElement limitElement))
{
if (limitElement.TryGetInt32(out int explicitLimit))
{
limit = explicitLimit;
}
}
string cursor = string.Empty;
if (requestBody.TryGetProperty("cursor", out JsonElement cursorElement))
{
cursor = cursorElement.ToString();
}
if (requestBody.TryGetProperty("queryFields", out JsonElement triggerFields))
{
// TODO: use queryFields.
}

return await GetJsonNodesAsync(cursor, limit);
}
catch (JsonException)
{
return BadRequest("Invalid JSON data.");
}
}

#if false
// POST: api/ifttt/v1/queries/orcanodes
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async Task<ActionResult<Orcanode>> PostOrcanode(Orcanode orcanode)
{
_databaseContext.Orcanodes.Add(orcanode);
await _databaseContext.SaveChangesAsync();

return CreatedAtAction("GetOrcanode", new { id = orcanode.ID }, orcanode);
}

// PUT: api/ifttt/v1/queries/orcanodes/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
public async Task<IActionResult> PutOrcanode(int id, Orcanode orcanode)
{
if (id != orcanode.ID)
{
return BadRequest();
}

_databaseContext.Entry(orcanode).State = EntityState.Modified;

try
{
await _databaseContext.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!OrcanodeExists(id))
{
return NotFound();
}
else
{
throw;
}
}

return NoContent();
}

// POST: api/ifttt/v1/queries/orcanodes
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async Task<ActionResult<Orcanode>> PostOrcanode(Orcanode orcanode)
{
_databaseContext.Orcanodes.Add(orcanode);
await _databaseContext.SaveChangesAsync();

return CreatedAtAction("GetOrcanode", new { id = orcanode.ID }, orcanode);
}

// DELETE: api/ifttt/v1/queries/orcanodes/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteOrcanode(int id)
{
var orcanode = await _databaseContext.Orcanodes.FindAsync(id);
if (orcanode == null)
{
return NotFound();
}

_databaseContext.Orcanodes.Remove(orcanode);
await _databaseContext.SaveChangesAsync();

return NoContent();
}

private bool OrcanodeExists(int id)
{
return _databaseContext.Orcanodes.Any(e => e.ID == id);
}
#endif
}
}
25 changes: 25 additions & 0 deletions OrcanodeMonitor/Api/StatusController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Mvc;
using OrcanodeMonitor.Core;
using System.Net;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace OrcanodeMonitor.Api
{
[Route("api/ifttt/v1/[controller]")]
[ApiController]
public class StatusController : ControllerBase
{
// GET: api/ifttt/v1/<StatusController>
[HttpGet]
public IActionResult Get()
{
var failure = Fetcher.CheckIftttServiceKey(Request);
if (failure != null)
{
return Unauthorized(failure);
}
return Ok();
}
}
}
39 changes: 39 additions & 0 deletions OrcanodeMonitor/Api/TestSetupController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Mvc;
using OrcanodeMonitor.Core;
using System.Net;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace OrcanodeMonitor.Api
{
[Route("api/ifttt/v1/test/setup")]
[ApiController]
public class TestSetupController : ControllerBase
{
// POST api/ifttt/v1/test/setup
[HttpPost]
public IActionResult Post()
{
var failure = Fetcher.CheckIftttServiceKey(Request);
if (failure != null)
{
return Unauthorized(failure);
}

var result = new
{
data = new
{
samples = new
{
triggers = new
{
}
}
}
};

return Ok(result);
}
}
}
Loading