Skip to content

Commit 91bf831

Browse files
authored
Cwdoe 1452 copy scenario events across msels (#118)
1 parent fd9fb71 commit 91bf831

File tree

3 files changed

+173
-5
lines changed

3 files changed

+173
-5
lines changed

Blueprint.Api/Blueprint.Api.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<Version>1.2.1</Version>
4+
<Version>1.3.0-rc1</Version>
55
<TargetFramework>net6.0</TargetFramework>
66
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
77
<NoWarn>CS1591</NoWarn>

Blueprint.Api/Controllers/ScenarioEventController.cs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Blueprint.Api.Infrastructure.QueryParameters;
1414
using Blueprint.Api.Services;
1515
using Swashbuckle.AspNetCore.Annotations;
16+
using Blueprint.Api.Migrations.PostgreSQL.Migrations;
1617

1718
namespace Blueprint.Api.Controllers
1819
{
@@ -106,10 +107,28 @@ public async Task<IActionResult> CreateFromInjects([FromBody] CreateFromInjectsF
106107
}
107108

108109
/// <summary>
109-
/// Updates an ScenarioEvent
110+
/// Copies ScenarioEvents from one MSEL to another
110111
/// </summary>
111112
/// <remarks>
112-
/// Updates an ScenarioEvent with the attributes specified
113+
/// Copies ScenarioEvents from the supplied list to another MSEL
114+
/// </remarks>
115+
/// <param name="scenarioEventIdList">The list of ScenarioEvent IDs</param>
116+
/// <param name="mselId">The MSEL ID</param>
117+
/// <param name="ct"></param>
118+
[HttpPost("msels/{mselId}/scenarioEvents/copy")]
119+
[ProducesResponseType(typeof(IEnumerable<ViewModels.ScenarioEvent>), (int)HttpStatusCode.Created)]
120+
[SwaggerOperation(OperationId = "copyScenarioEventsToMsel")]
121+
public async Task<IActionResult> CreateFromInjects([FromRoute] Guid mselId, [FromBody] List<Guid> scenarioEventIdList, CancellationToken ct)
122+
{
123+
var list = await _scenarioEventService.CopyScenarioEventsToMselAsync(mselId, scenarioEventIdList, ct);
124+
return Ok(list);
125+
}
126+
127+
/// <summary>
128+
/// Updates a ScenarioEvent
129+
/// </summary>
130+
/// <remarks>
131+
/// Updates a ScenarioEvent with the attributes specified
113132
/// <para />
114133
/// Accessible only to a SuperUser or a User on an Admin Team within the specified ScenarioEvent
115134
/// </remarks>
@@ -127,10 +146,10 @@ public async Task<IActionResult> Update([FromRoute] Guid id, [FromBody] ViewMode
127146
}
128147

129148
/// <summary>
130-
/// Deletes an ScenarioEvent
149+
/// Deletes a ScenarioEvent
131150
/// </summary>
132151
/// <remarks>
133-
/// Deletes an ScenarioEvent with the specified id
152+
/// Deletes a ScenarioEvent with the specified id
134153
/// <para />
135154
/// Accessible only to a SuperUser or a User on an Admin Team within the specified ScenarioEvent
136155
/// </remarks>

Blueprint.Api/Services/ScenarioEventService.cs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
using Blueprint.Api.Infrastructure.Options;
2121
using Blueprint.Api.ViewModels;
2222
using Blueprint.Api.Infrastructure.JsonConverters;
23+
using NuGet.Packaging.Licenses;
24+
using System.Data;
2325

2426
namespace Blueprint.Api.Services
2527
{
@@ -29,6 +31,7 @@ public interface IScenarioEventService
2931
Task<ViewModels.ScenarioEvent> GetAsync(Guid id, CancellationToken ct);
3032
Task<IEnumerable<ViewModels.ScenarioEvent>> CreateAsync(ViewModels.ScenarioEvent scenarioEvent, CancellationToken ct);
3133
Task<IEnumerable<ViewModels.ScenarioEvent>> CreateFromInjectsAsync(CreateFromInjectsForm createFromInjectsForm, CancellationToken ct);
34+
Task<IEnumerable<ViewModels.ScenarioEvent>> CopyScenarioEventsToMselAsync(Guid mselId, List<Guid> scenarioEventIdList, CancellationToken ct);
3235
Task<IEnumerable<ViewModels.ScenarioEvent>> UpdateAsync(Guid id, ViewModels.ScenarioEvent scenarioEvent, CancellationToken ct);
3336
Task<bool> DeleteAsync(Guid id, CancellationToken ct);
3437
Task<bool> BatchDeleteAsync(Guid[] idList, CancellationToken ct);
@@ -339,6 +342,152 @@ public ScenarioEventService(
339342
return _mapper.Map<IEnumerable<ViewModels.ScenarioEvent>>(scenarioEventEnitities);
340343
}
341344

345+
public async Task<IEnumerable<ViewModels.ScenarioEvent>> CopyScenarioEventsToMselAsync(Guid mselId, List<Guid> scenarioEventIdList, CancellationToken ct)
346+
{
347+
// make sure destination MSEL exists
348+
var destinationMsel = await _context.Msels
349+
.Include(m => m.DataFields)
350+
.SingleOrDefaultAsync(v => v.Id == mselId, ct);
351+
if (destinationMsel == null)
352+
throw new EntityNotFoundException<ScenarioEventEntity>($"MSEL not found {mselId}.");
353+
354+
// make sure the source MSEL and all scenarioEvents exist
355+
var sourceMsel = await _context.ScenarioEvents
356+
.Where(m => m.Id == scenarioEventIdList[0])
357+
.Include(m => m.Msel)
358+
.ThenInclude(m => m.DataFields)
359+
.ThenInclude(f => f.DataOptions)
360+
.Include(m => m.Msel)
361+
.ThenInclude(m => m.ScenarioEvents)
362+
.ThenInclude(s => s.DataValues)
363+
.Select(m => m.Msel)
364+
.AsSplitQuery()
365+
.AsNoTracking()
366+
.SingleOrDefaultAsync(ct);
367+
368+
// user must be a Content Developer or a MSEL owner for both source and destination MSELs
369+
if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded &&
370+
!(await MselOwnerRequirement.IsMet(_user.GetId(), mselId, _context) &&
371+
await MselOwnerRequirement.IsMet(_user.GetId(), sourceMsel.Id, _context)))
372+
throw new ForbiddenException();
373+
374+
// get the sourceScenarioEvents
375+
var sourceScenarioEvents = sourceMsel.ScenarioEvents.Where(s => scenarioEventIdList.Contains(s.Id));
376+
if (sourceMsel == null || sourceScenarioEvents.Count() != scenarioEventIdList.Count())
377+
throw new DataException("The list of Scenario Event IDs was invalid.");
378+
379+
// determine which data fields are used by these scenario events
380+
var neededDataFields = new List<Guid>();
381+
foreach (var scenarioEvent in sourceScenarioEvents)
382+
{
383+
foreach (var dataValue in scenarioEvent.DataValues)
384+
{
385+
if (dataValue.Value != null && !neededDataFields.Contains(dataValue.DataFieldId))
386+
{
387+
neededDataFields.Add(dataValue.DataFieldId);
388+
}
389+
}
390+
}
391+
// set some initial values
392+
var userId = _user.GetId();
393+
var dateCreated = DateTime.UtcNow;
394+
var dataFieldDictionary = new Dictionary<Guid, Guid>();
395+
var displayOrder = destinationMsel.DataFields.Count > 0
396+
? destinationMsel.DataFields.Max(m => m.DisplayOrder)
397+
: 0;
398+
399+
// start a transaction
400+
await _context.Database.BeginTransactionAsync();
401+
402+
// get the data field map (create new data fields as necessary)
403+
foreach (var sourceDataField in sourceMsel.DataFields.Where(m => neededDataFields.Contains(m.Id)))
404+
{
405+
var destinationDataField = destinationMsel.DataFields.SingleOrDefault(m => m.Name == sourceDataField.Name && m.DataType == sourceDataField.DataType);
406+
if (destinationDataField == null)
407+
{
408+
destinationDataField = new DataFieldEntity()
409+
{
410+
Id = Guid.NewGuid(),
411+
MselId = destinationMsel.Id,
412+
Name = sourceDataField.Name,
413+
DataType = sourceDataField.DataType,
414+
DisplayOrder = ++displayOrder,
415+
DateCreated = dateCreated,
416+
CreatedBy = userId,
417+
IsChosenFromList = sourceDataField.IsChosenFromList,
418+
OnScenarioEventList = sourceDataField.OnScenarioEventList,
419+
OnExerciseView = sourceDataField.OnExerciseView,
420+
};
421+
_context.DataFields.Add(destinationDataField);
422+
if (sourceDataField.IsChosenFromList)
423+
{
424+
foreach (var sourceDataOption in sourceDataField.DataOptions)
425+
{
426+
var destinationDataOption = new DataOptionEntity()
427+
{
428+
Id = Guid.NewGuid(),
429+
DataFieldId = destinationDataField.Id,
430+
OptionName = sourceDataOption.OptionName,
431+
OptionValue = sourceDataOption.OptionValue,
432+
DisplayOrder = sourceDataOption.DisplayOrder,
433+
};
434+
_context.DataOptions.Add(destinationDataOption);
435+
}
436+
}
437+
}
438+
dataFieldDictionary[destinationDataField.Id] = sourceDataField.Id;
439+
}
440+
await _context.SaveChangesAsync(ct);
441+
// get the new list of data field IDs
442+
var dataFieldIdList = _context.DataFields
443+
.Where(df => df.MselId == destinationMsel.Id)
444+
.Select(df => df.Id);
445+
// Loop through the source scenario events
446+
foreach (var sourceScenarioEvent in sourceScenarioEvents)
447+
{
448+
var destinationScenarioEvent = new ScenarioEventEntity()
449+
{
450+
Id = Guid.NewGuid(),
451+
MselId = destinationMsel.Id,
452+
GroupOrder = 0,
453+
IsHidden = false,
454+
DeltaSeconds = sourceScenarioEvent.DeltaSeconds,
455+
ScenarioEventType = sourceScenarioEvent.ScenarioEventType,
456+
Description = sourceScenarioEvent.Description,
457+
InjectId = sourceScenarioEvent.InjectId,
458+
DateCreated = dateCreated,
459+
CreatedBy = userId,
460+
};
461+
_context.ScenarioEvents.Add(destinationScenarioEvent);
462+
// create blank data values
463+
foreach (var dataFieldId in dataFieldIdList)
464+
{
465+
var dataValue = new DataValueEntity();
466+
dataValue.DataFieldId = dataFieldId;
467+
dataValue.Id = Guid.NewGuid();
468+
dataValue.ScenarioEventId = destinationScenarioEvent.Id;
469+
dataValue.CreatedBy = userId;
470+
dataValue.DateCreated = dateCreated;
471+
dataValue.DateModified = null;
472+
dataValue.ModifiedBy = null;
473+
if (dataFieldDictionary.Keys.Contains(dataFieldId))
474+
{
475+
dataValue.Value = sourceScenarioEvent.DataValues.Single(m => m.DataFieldId == dataFieldDictionary[dataFieldId]).Value;
476+
}
477+
_context.DataValues.Add(dataValue);
478+
}
479+
}
480+
await _context.SaveChangesAsync(ct);
481+
// commit the transaction
482+
await _context.Database.CommitTransactionAsync(ct);
483+
// return all msel scenarioEvents
484+
var scenarioEventEnitities = await _context.ScenarioEvents
485+
.Include(m => m.DataValues)
486+
.Where(m => m.MselId == destinationMsel.Id)
487+
.ToListAsync(ct);
488+
return _mapper.Map<IEnumerable<ViewModels.ScenarioEvent>>(scenarioEventEnitities);
489+
}
490+
342491
public async Task<IEnumerable<ViewModels.ScenarioEvent>> UpdateAsync(Guid id, ViewModels.ScenarioEvent scenarioEvent, CancellationToken ct)
343492
{
344493
var canUpdateScenarioEvent = (await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded ||

0 commit comments

Comments
 (0)