Select Git revision
MetadataController.cs
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
MetadataController.cs 11.92 KiB
using Coscine.Action;
using Coscine.Action.EventArgs;
using Coscine.Api.Metadata.ParameterObjects;
using Coscine.ApiCommons;
using Coscine.ApiCommons.Exceptions;
using Coscine.Configuration;
using Coscine.Database.Models;
using Coscine.Database.Util;
using Coscine.Metadata;
using GitLabApiClient;
using GitLabApiClient.Models.Branches.Requests;
using GitLabApiClient.Models.Commits.Requests.CreateCommitRequest;
using GitLabApiClient.Models.MergeRequests.Requests;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using VDS.RDF;
using VDS.RDF.Parsing;
using VDS.RDF.Writing;
namespace Coscine.Api.Metadata.Controllers
{
/// <summary>
/// This controller represents the actions which can be performed with a Metadata storage.
/// </summary>
[Authorize]
public class MetadataController : Controller
{
private readonly Authenticator _authenticator;
private readonly Emitter _emitter;
private readonly IConfiguration _configuration;
private readonly ResourceModel _resourceModel;
private readonly RdfStoreConnector _rdfStoreConnector;
private static readonly string HostUrl = "https://git.rwth-aachen.de/";
private readonly string ApplicationProfileUrl = "https://purl.org/coscine/ap/";
private static readonly string ApplicationProfileProjectURL = HttpUtility.UrlEncode("coscine/graphs/applicationprofiles");
/// <summary>
/// MetadataController constructor specifying an authenticator and a ResourceModel.
/// </summary>
public MetadataController()
{
_configuration = Program.Configuration;
_authenticator = new Authenticator(this, _configuration);
_resourceModel = new ResourceModel();
_rdfStoreConnector = new RdfStoreConnector(_configuration.GetStringAndWait("coscine/local/virtuoso/additional/url"));
_emitter = new Emitter(_configuration);
}
/// <summary>
/// This method returns all application profiles.
/// </summary>
/// <returns>profiles</returns>
[HttpGet("[controller]/profiles/")]
[AllowAnonymous]
public IActionResult GetProfiles()
{
var applicationProfiles = _rdfStoreConnector.GetApplicationProfiles();
return Json(new JArray(applicationProfiles));
}
/// <summary>
/// This method returns the application profile for the given profileUrl.
/// </summary>
/// <param name="profile">Url of the application profile</param>
/// <returns>Json with the application profile</returns>
[HttpGet("[controller]/profiles/{profile}")]
public IActionResult GetProfile(string profile)
{
var profileUrl = profile.StartsWith("http") ? HttpUtility.UrlDecode(profile) : $"{ApplicationProfileUrl}{profile}/";
if (!profileUrl.StartsWith(ApplicationProfileUrl))
{
return StatusCode((int)HttpStatusCode.Forbidden, $"Profile has to start with {ApplicationProfileUrl}!");
}
var graph = _rdfStoreConnector.GetGraph(profileUrl);
var tripleStore = new TripleStore();
tripleStore.Add(graph);
var outStoreJson = VDS.RDF.Writing.StringWriter.Write(tripleStore, new JsonLdWriter());
var json = JToken.Parse(outStoreJson);
return Ok(json);
}
/// <summary>
/// Returns the application profile with the fixed values for the given resource.
/// </summary>
/// <param name="profile">Url of the application profile</param>
/// <param name="resourceId">Id of the resource</param>
/// <returns>JSON with the application profile or StatusCode 403</returns>
[HttpGet("[controller]/profiles/{profile}/{resourceId}")]
public IActionResult GetApplicationProfileComplete(string profile, string resourceId)
{
var user = _authenticator.GetUser();
var resource = _resourceModel.GetById(Guid.Parse(resourceId));
if (user == null || !_resourceModel.HasAccess(user, resource, UserRoles.Owner, UserRoles.Member))
{
return StatusCode((int)HttpStatusCode.Forbidden, "User is no project member!");
}
var profileUrl = profile.StartsWith("http") ? HttpUtility.UrlDecode(profile) : $"{ApplicationProfileUrl}{profile}/";
if (!profileUrl.StartsWith(ApplicationProfileUrl))
{
return StatusCode((int)HttpStatusCode.Forbidden, $"Profile has to start with {ApplicationProfileUrl}!");
}
var graph = _rdfStoreConnector.GetGraph(profileUrl);
var fixedValuesGraph = new Graph();
fixedValuesGraph.LoadFromString(resource.FixedValues, new RdfJsonParser());
graph.Merge(fixedValuesGraph);
var tripleStore = new TripleStore();
tripleStore.Add(graph);
var outStoreJson = VDS.RDF.Writing.StringWriter.Write(tripleStore, new JsonLdWriter());
var json = JToken.Parse(outStoreJson);
return Ok(json);
}
/// <summary>
/// This method returns a list of all vocabularies.
/// </summary>
/// <throws>Exception for the code that has not been implemented</throws>
[HttpGet("[controller]/vocabularies/")]
public IActionResult GetVocabularies()
{
throw new NotImplementedException();
}
/// <summary>
/// This method returns a specific vocabulary.
/// </summary>
/// <param name="path">Url of the vocabulary</param>
/// <returns>JSON with the requested vocabulary</returns>
[HttpGet("[controller]/vocabularies/{path}")]
public IActionResult GetVocabulary(string path)
{
var graph = _rdfStoreConnector.GetGraph(HttpUtility.UrlDecode(path));
var de = new JArray();
foreach (var kv in _rdfStoreConnector.GetVocabularyLabels(graph, "de"))
{
JObject obj = new JObject
{
["value"] = kv.Key,
["name"] = kv.Value
};
de.Add(obj);
}
var en = new JArray();
foreach (var kv in _rdfStoreConnector.GetVocabularyLabels(graph, "en"))
{
JObject obj = new JObject
{
["value"] = kv.Key,
["name"] = kv.Value
};
en.Add(obj);
}
JObject json = new JObject
{
["de"] = de,
["en"] = en
};
return Json(json);
}
/// <summary>
/// This method returns instances.
/// </summary>
/// <param name="projectId">Id of the project</param>
/// <param name="className">class name</param>
/// <returns>instances as Json, or throw an Exception if the user has not beed authorized</returns>
[HttpGet("[controller]/instances/{projectId}/{className}")]
public IActionResult GetClassInstances(Guid projectId, string className)
{
var user = _authenticator.GetUser();
ProjectModel projectModel = new ProjectModel();
if (projectModel.HasAccess(user, projectModel.GetById(projectId), UserRoles.Owner, UserRoles.Member))
{
if (!Uri.TryCreate(HttpUtility.UrlDecode(className), UriKind.Absolute, out Uri uri))
{
throw new ArgumentException("ClassName is not a valid Uri.");
}
var graph = _rdfStoreConnector.GetClassGraph(uri);
var de = new JArray();
foreach (var kv in _rdfStoreConnector.GetVocabularyLabels(graph, "de"))
{
JObject obj = new JObject
{
["value"] = kv.Key,
["name"] = kv.Value
};
de.Add(obj);
}
var en = new JArray();
foreach (var kv in _rdfStoreConnector.GetVocabularyLabels(graph, "en"))
{
JObject obj = new JObject
{
["value"] = kv.Key,
["name"] = kv.Value
};
en.Add(obj);
}
JObject json = new JObject
{
["de"] = de,
["en"] = en
};
return Json(json);
}
else
{
throw new NotAuthorizedException("User is no project member!");
}
}
/// <summary>
/// Create a request for storing a given application profile.
/// </summary>
/// <param name="applicationProfile">Object describing the application profile</param>
/// <returns>NoContent</returns>
[HttpPut("[controller]/profiles/")]
public async Task<IActionResult> SaveApplicationProfileAsync([FromBody] ApplicationProfile applicationProfile)
{
var user = _authenticator.GetUser();
if (string.IsNullOrWhiteSpace(user.EmailAddress))
{
return BadRequest("The user's email has to be set!");
}
var token = _configuration.GetStringAndWait("coscine/global/gitlabtoken");
var gitLabClient = new GitLabClient(HostUrl, token);
var newBranchName = "Request/" + Guid.NewGuid().ToString();
if (!applicationProfile.BaseURL.StartsWith(ApplicationProfileUrl))
{
return BadRequest($"The Application Profile URL has to start with {ApplicationProfileUrl}.");
}
if (applicationProfile.MimeType.ToLower().Trim() != "text/turtle")
{
return BadRequest("The mime type has to be text/turtle");
}
await gitLabClient.Branches.CreateAsync(ApplicationProfileProjectURL, new CreateBranchRequest(newBranchName, "master"));
var folderName = applicationProfile.BaseURL.Replace(ApplicationProfileUrl, "");
if (folderName.EndsWith("/"))
{
folderName = folderName[..(folderName.Length - 1)];
}
var actions = new List<CreateCommitRequestAction>
{
new CreateCommitRequestAction(
new CreateCommitRequestActionType(),
$"profiles/{folderName}/index.ttl") {
Content = applicationProfile.Definition
}
};
var commitRequest = new CreateCommitRequest(newBranchName, $"{user.DisplayName} requests new application profile {applicationProfile.Name}", actions)
{
AuthorEmail = user.EmailAddress,
AuthorName = user.DisplayName
};
await gitLabClient.Commits.CreateAsync(ApplicationProfileProjectURL, commitRequest, false);
var newMergeRequest = await gitLabClient.MergeRequests.CreateAsync(ApplicationProfileProjectURL,
new CreateMergeRequest(newBranchName, "master", $"Merge request for new application profile {applicationProfile.Name}")
{
Description = $"Merge request created by {user.DisplayName}, {user.EmailAddress}, created at {DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss tt")}"
});
_emitter.EmitApplicationProfileRequest(new ApplicationProfileEventArgs(_configuration)
{
RequestOwner = user,
ApplicationProfileName = applicationProfile.Name,
MergeRequestURL = newMergeRequest.WebUrl
});
return NoContent();
}
}
}