Skip to content

Commit

Permalink
- Adds a call when Gameboard creates challenge data for Unity games t…
Browse files Browse the repository at this point in the history
…o update the console URLs in gamebrain. (#63)

- Gameboard now correctly only creates one copy of the Unity challenge per team, designing either the manager or the calling player as the team leader.
  • Loading branch information
sei-bstein authored Nov 10, 2022
1 parent 0404712 commit 4109fa8
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 64 deletions.
53 changes: 26 additions & 27 deletions src/Gameboard.Api/Features/Challenge/ChallengeController.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
// Copyright 2021 Carnegie Mellon University. All Rights Reserved.
// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

using Gameboard.Api.Hubs;
using Gameboard.Api.Services;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.AspNetCore.Authorization;
using TopoMojo.Api.Client;
using Gameboard.Api.Validators;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Gameboard.Api.Hubs;
using System.Collections.Generic;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using TopoMojo.Api.Client;

namespace Gameboard.Api.Controllers
{
Expand All @@ -32,7 +31,7 @@ public ChallengeController(
PlayerService playerService,
IHubContext<AppHub, IAppHubEvent> hub,
ConsoleActorMap actormap
): base(logger, cache, validator)
) : base(logger, cache, validator)
{
ChallengeService = challengeService;
PlayerService = playerService;
Expand Down Expand Up @@ -83,14 +82,14 @@ await Hub.Clients.Group(result.TeamId).ChallengeEvent(
/// <returns></returns>
[HttpGet("api/challenge/{id}")]
[Authorize]
public async Task<Challenge> Retrieve([FromRoute]string id)
public async Task<Challenge> Retrieve([FromRoute] string id)
{
AuthorizeAny(
() => Actor.IsDirector,
() => ChallengeService.UserIsTeamPlayer(id, Actor.Id).Result
);

await Validate(new Entity{ Id = id });
await Validate(new Entity { Id = id });

return await ChallengeService.Retrieve(id);
}
Expand All @@ -102,7 +101,7 @@ public async Task<Challenge> Retrieve([FromRoute]string id)
/// <returns></returns>
[HttpPost("api/challenge/preview")]
[Authorize]
public async Task<Challenge> Preview([FromBody]NewChallenge model)
public async Task<Challenge> Preview([FromBody] NewChallenge model)
{
AuthorizeAny(
() => IsSelf(model.PlayerId).Result
Expand Down Expand Up @@ -133,14 +132,14 @@ public Task Update([FromBody] ChangedChallenge model)
/// <returns></returns>
[HttpDelete("/api/challenge/{id}")]
[Authorize]
public async Task Delete([FromRoute]string id)
public async Task Delete([FromRoute] string id)
{
AuthorizeAny(
() => Actor.IsDirector
// () => Actor.IsTester && ChallengeService.UserIsTeamPlayer(id, Actor.Id).Result
);

await Validate(new Entity{ Id = id });
await Validate(new Entity { Id = id });

await ChallengeService.Delete(id);
return;
Expand All @@ -153,7 +152,7 @@ public async Task Delete([FromRoute]string id)
/// <returns></returns>
[HttpPut("/api/challenge/start")]
[Authorize]
public async Task<Challenge> StartGamespace([FromBody]ChangedChallenge model)
public async Task<Challenge> StartGamespace([FromBody] ChangedChallenge model)
{
AuthorizeAny(
() => Actor.IsDirector,
Expand All @@ -178,14 +177,14 @@ await Hub.Clients.Group(result.TeamId).ChallengeEvent(
/// <returns></returns>
[HttpPut("/api/challenge/stop")]
[Authorize]
public async Task<Challenge> StopGamespace([FromBody]ChangedChallenge model)
public async Task<Challenge> StopGamespace([FromBody] ChangedChallenge model)
{
AuthorizeAny(
() => Actor.IsDirector,
() => ChallengeService.UserIsTeamPlayer(model.Id, Actor.Id).Result
);

await Validate(new Entity{ Id = model.Id });
await Validate(new Entity { Id = model.Id });

var result = await ChallengeService.StopGamespace(model.Id, Actor.Id);

Expand All @@ -203,15 +202,15 @@ await Hub.Clients.Group(result.TeamId).ChallengeEvent(
/// <returns></returns>
[HttpPut("/api/challenge/grade")]
[Authorize(AppConstants.GraderPolicy)]
public async Task<Challenge> Grade([FromBody]SectionSubmission model)
public async Task<Challenge> Grade([FromBody] SectionSubmission model)
{
AuthorizeAny(
() => Actor.IsDirector,
() => Actor.Id == model.Id, // auto-grader
() => ChallengeService.UserIsTeamPlayer(model.Id, Actor.Id).Result
);

await Validate(new Entity{ Id = model.Id });
await Validate(new Entity { Id = model.Id });

var result = await ChallengeService.Grade(model, Actor.Id);

Expand All @@ -229,7 +228,7 @@ await Hub.Clients.Group(result.TeamId).ChallengeEvent(
/// <returns></returns>
[HttpPut("/api/challenge/regrade")]
[Authorize]
public async Task<Challenge> Regrade([FromBody]Entity model)
public async Task<Challenge> Regrade([FromBody] Entity model)
{
AuthorizeAny(
() => Actor.IsDirector
Expand All @@ -253,7 +252,7 @@ await Hub.Clients.Group(result.TeamId).ChallengeEvent(
/// <returns></returns>
[HttpGet("/api/challenge/{id}/audit")]
[Authorize]
public async Task<SectionSubmission[]> Audit([FromRoute]string id)
public async Task<SectionSubmission[]> Audit([FromRoute] string id)
{
AuthorizeAny(
() => Actor.IsDirector
Expand All @@ -271,7 +270,7 @@ public async Task<SectionSubmission[]> Audit([FromRoute]string id)
/// <returns></returns>
[HttpPost("/api/challenge/console")]
[Authorize(AppConstants.ConsolePolicy)]
public async Task<ConsoleSummary> GetConsole([FromBody]ConsoleRequest model)
public async Task<ConsoleSummary> GetConsole([FromBody] ConsoleRequest model)
{
await Validate(new Entity { Id = model.SessionId });

Expand Down Expand Up @@ -301,7 +300,7 @@ await ChallengeService.SetConsoleActor(model, Actor.Id, Actor.ApprovedName)
/// <returns></returns>
[HttpPut("/api/challenge/console")]
[Authorize(AppConstants.ConsolePolicy)]
public async Task SetConsoleActor([FromBody]ConsoleRequest model)
public async Task SetConsoleActor([FromBody] ConsoleRequest model)
{
await Validate(new Entity { Id = model.SessionId });

Expand All @@ -315,7 +314,7 @@ await ChallengeService.SetConsoleActor(model, Actor.Id, Actor.ApprovedName)

[HttpGet("/api/challenge/consoles")]
[Authorize]
public async Task<List<ObserveChallenge>> FindConsoles([FromQuery]string gid)
public async Task<List<ObserveChallenge>> FindConsoles([FromQuery] string gid)
{
AuthorizeAny(
() => Actor.IsDirector,
Expand All @@ -327,7 +326,7 @@ public async Task<List<ObserveChallenge>> FindConsoles([FromQuery]string gid)

[HttpGet("/api/challenge/consoleactors")]
[Authorize]
public ConsoleActor[] GetConsoleActors([FromQuery]string gid)
public ConsoleActor[] GetConsoleActors([FromQuery] string gid)
{
AuthorizeAny(
() => Actor.IsDirector,
Expand All @@ -339,7 +338,7 @@ public ConsoleActor[] GetConsoleActors([FromQuery]string gid)

[HttpGet("/api/challenge/consoleactor")]
[Authorize(AppConstants.ConsolePolicy)]
public ConsoleActor GetConsoleActor([FromQuery]string uid)
public ConsoleActor GetConsoleActor([FromQuery] string uid)
{
AuthorizeAny(
() => Actor.IsDirector,
Expand Down Expand Up @@ -402,7 +401,7 @@ public async Task<ArchivedChallenge[]> ListArchived([FromQuery] SearchFilter mod

private async Task<bool> IsSelf(string playerId)
{
return await PlayerService.MapId(playerId) == Actor.Id;
return await PlayerService.MapId(playerId) == Actor.Id;
}
}
}
2 changes: 1 addition & 1 deletion src/Gameboard.Api/Features/UnityGames/IUnityGameService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Gameboard.Api.Features.UnityGames;

public interface IUnityGameService
{
Task<IEnumerable<ChallengeEvent>> AddChallengeEvent(NewUnityChallengeEvent model, string userId);
Task<ChallengeEvent> AddChallengeEvent(NewUnityChallengeEvent model, string userId);
Task<Data.Challenge> AddChallenge(NewUnityChallenge newChallenge, User actor);
Task CreateMissionEvent(UnityMissionUpdate model, Api.User actor);
Task DeleteChallengeData(string gameId);
Expand Down
3 changes: 1 addition & 2 deletions src/Gameboard.Api/Features/UnityGames/IUnityStore.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Gameboard.Api.Data.Abstractions;

namespace Gameboard.Api.Features.UnityGames;

public interface IUnityStore : IStore<Data.ChallengeSpec>
{
Task<IEnumerable<Data.ChallengeEvent>> AddUnityChallengeEvents(IEnumerable<Data.ChallengeEvent> challengeEvents);
Task<Data.ChallengeEvent> AddUnityChallengeEvent(Data.ChallengeEvent challengeEvent);
}
56 changes: 50 additions & 6 deletions src/Gameboard.Api/Features/UnityGames/UnityGameController.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Copyright 2021 Carnegie Mellon University. All Rights Reserved.
// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.

using System.Collections.Generic;
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Threading.Tasks;
using AutoMapper;
using Gameboard.Api.Features.UnityGames;
Expand All @@ -13,6 +15,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
Expand All @@ -22,10 +25,12 @@ namespace Gameboard.Api.Controllers;
[Authorize]
public class UnityGameController : _Controller
{
private readonly CoreOptions _appSettings;
private readonly ConsoleActorMap _actorMap;
private readonly GameService _gameService;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IHubContext<AppHub, IAppHubEvent> _hub;
private readonly LinkGenerator _linkGenerator;
private readonly IMapper _mapper;
private readonly IUnityGameService _unityGameService;

Expand All @@ -36,21 +41,32 @@ public UnityGameController(
UnityGamesValidator validator,
// other stuff
ConsoleActorMap actorMap,
CoreOptions appSettings,
GameService gameService,
IHttpClientFactory httpClientFactory,
IUnityGameService unityGameService,
IHubContext<AppHub, IAppHubEvent> hub,
LinkGenerator link,
IMapper mapper
) : base(logger, cache, validator)
{
_actorMap = actorMap;
_appSettings = appSettings;
_gameService = gameService;
_httpClientFactory = httpClientFactory;
_hub = hub;
_linkGenerator = link;
_mapper = mapper;
_unityGameService = unityGameService;
}

[HttpGet("/api/unity")]
[AllowAnonymous]
public IActionResult Hi()
{
return Ok(_appSettings.GameEngineUrl + " " + Request.GetTypedHeaders().Referer.ToString());
}

[HttpGet("/api/unity/{gid}/{tid}")]
[Authorize]
public async Task<IActionResult> GetGamespace([FromRoute] string gid, [FromRoute] string tid)
Expand Down Expand Up @@ -113,7 +129,7 @@ public async Task<string> UndeployUnitySpace([FromQuery] string gid, [FromRoute]
/// <param name="model">NewChallengeEvent</param>
/// <returns>ChallengeEvent</returns>
[Authorize]
[HttpPost("api/unity/challenges")]
[HttpPost("api/unity/challenge")]
public async Task<Data.Challenge> CreateChallenge([FromBody] NewUnityChallenge model)
{
AuthorizeAny(
Expand All @@ -125,9 +141,37 @@ public async Task<string> UndeployUnitySpace([FromQuery] string gid, [FromRoute]
await Validate(model);
var result = await _unityGameService.AddChallenge(model, Actor);

await _hub.Clients
// now that we have challenge IDs, we can update gamebrain's console urls
var gamebrainClient = await CreateGamebrain();

var vmData = model.Vms.Select(vm =>
{
var consoleHost = new UriBuilder(Request.Scheme, Request.Host.Host, Request.Host.Port ?? -1, "test/gb/mks");
consoleHost.Query = $"f=1&s={result.Id}&v={vm.Name}";
return new UnityGameVm
{
Id = vm.Id,
Url = consoleHost.Uri.ToString(),
Name = vm.Name,
};
}).ToArray();

try
{
await gamebrainClient.PostAsync($"admin/update_console_urls/{model.TeamId}", JsonContent.Create<UnityGameVm[]>(vmData, mediaType: MediaTypeHeaderValue.Parse("application/json")));
}
catch (Exception ex)
{
Console.Write("Calling gamebrain failed with", ex);
}
finally
{
// notify the hub (if there is one)
await _hub.Clients
.Group(model.TeamId)
.ChallengeEvent(new HubEvent<Challenge>(_mapper.Map<Challenge>(result), EventAction.Updated));
}

return result;
}
Expand All @@ -137,9 +181,9 @@ await _hub.Clients
/// </summary>
/// <param name="model">NewChallengeEvent</param>
/// <returns>ChallengeEvent</returns>
[HttpPost("api/unity/challengeEvents")]
[HttpPost("api/unity/challengeEvent")]
[Authorize]
public async Task<IEnumerable<Data.ChallengeEvent>> CreateChallengeEvent([FromBody] NewUnityChallengeEvent model)
public async Task<Data.ChallengeEvent> CreateChallengeEvent([FromBody] NewUnityChallengeEvent model)
{
AuthorizeAny(
() => Actor.IsDirector,
Expand All @@ -151,8 +195,8 @@ await _hub.Clients
return await _unityGameService.AddChallengeEvent(model, Actor.Id);
}

[Authorize]
[HttpPost("api/unity/mission-update")]
[Authorize]
public async Task CreateMissionEvent([FromBody] UnityMissionUpdate model)
{
AuthorizeAny(
Expand Down
Loading

0 comments on commit 4109fa8

Please sign in to comment.