Skip to content
Snippets Groups Projects
ApiController.cs 13.60 KiB
using System.ComponentModel.DataAnnotations;
using System.Text;
using Antlr4.Runtime;
using Antlr4.Runtime.Tree;
using CodeParser.Cpp;
using Database;
using Database.Model;
using DiagramBuilder.AbstractCreator;
using DiagramBuilder.ConcreteCreator;
using Microsoft.AspNetCore.Mvc;
using WebAPI.DTO;
using WebAPI.DTO.External;
using WebAPI.Utils;

namespace WebAPI.Controllers
{
    [ApiController]
    [Route("api/")]
    public class ApiController : ControllerBase
    {
        private readonly ILogger<ApiController> _logger;
        private readonly IConfiguration _configuration;
        private int tokenValidityMinutes => _configuration.GetValue<int>("UserManagement:TokenValidityMinutes");

        public ApiController(ILogger<ApiController> logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;
            using var db = new DatabaseContext();
            DatabaseContext.EnsureSeedData(db);
        }

        #region SourceCodeTransformation
        //convert-to-activity-diagram
        [HttpGet("convert-to-activity-diagram", Name = "ConvertToActivityDiagram")]
        [Produces("application/xml")]
        public IActionResult ConvertToActivityDiagram(
            [Required(ErrorMessage = "Token is required")]
            [RegularExpression(@"^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$", ErrorMessage = "Invalid GUID format for Token")]
            string token,
            
            [Required(ErrorMessage = "SourceCodeId is required")]
            [Range(1, int.MaxValue, ErrorMessage = "Id must be greater than 0")]
            int sourceCodeId,
            
            [Required(ErrorMessage = "FunctionIdentifier is required")]
            string functionIdentifier)
        {
            if (TokenManager.VerifyAndRevalidateToken(token, tokenValidityMinutes))
            {
                var user = TokenManager.GetUser(token, tokenValidityMinutes);
                using var db = new DatabaseContext();

                var sourceCode = db.SourceCodes.FirstOrDefault(s => s.Id == sourceCodeId && s.UserId == user.Id);
                if (sourceCode == null)
                {
                    return NotFound("Code does not exist");
                }

                //base 64 string to string
                string code = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(sourceCode.Code));

                var inputStream = new AntlrInputStream(code);
                var lexer = new CPP14Lexer(inputStream);
                CommonTokenStream tokens = new CommonTokenStream(lexer);
                CPP14Parser parser = new CPP14Parser(tokens);

                ActivityDiagramBuilderCreator activityDiagramBuilderCreator = new ActivityDiagramBuilderCreatorFactory();

                var diagramBuilder = activityDiagramBuilderCreator.FactoryMethod("ActivityDiagram");
                IParseTree tree = parser.translationUnit();

                if (parser.NumberOfSyntaxErrors == 0)
                {
                    CppActivityDiagramVisitor activityDiagramVisitor = new CppActivityDiagramVisitor(diagramBuilder, functionIdentifier);
                    Console.WriteLine(activityDiagramVisitor.Visit(tree));
                }

                var xmi = diagramBuilder.BuildDiagram();
                byte[] xmiBytes = Encoding.UTF8.GetBytes(xmi);
                var xmiStream = new MemoryStream(xmiBytes);
                
                return File(xmiStream, "application/xml", "activity_diagram.xmi");
            }
            else
            {
                return BadRequest("Invalid token");
            }
        }
        
        //convert-to-class-diagram 
        [HttpGet("convert-to-class-diagram", Name = "ConvertToClassDiagram")]
        [Produces("application/xml")]
        public IActionResult ConvertToClassDiagram([Required(ErrorMessage = "Token is required")]
            [RegularExpression(@"^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$", ErrorMessage = "Invalid GUID format for Token")]
            string token,
            
            [Required(ErrorMessage = "SourceCodeId is required")]
            [Range(1, int.MaxValue, ErrorMessage = "Id must be greater than 0")]
            int sourceCodeId)
        {
            if (TokenManager.VerifyAndRevalidateToken(token, tokenValidityMinutes))
            {
                var user = TokenManager.GetUser(token, tokenValidityMinutes);
                using var db = new DatabaseContext();

                var sourceCode = db.SourceCodes.FirstOrDefault(s => s.Id == sourceCodeId && s.UserId == user.Id);
                if (sourceCode == null)
                {
                    return NotFound("Code does not exist");
                }

                //base 64 string to string
                string code = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(sourceCode.Code));

                var inputStream = new AntlrInputStream(code);
                var lexer = new CPP14Lexer(inputStream);
                CommonTokenStream tokens = new CommonTokenStream(lexer);
                CPP14Parser parser = new CPP14Parser(tokens);

                ClassDiagramBuilderCreatorFactory classDiagramBuilderCreator = new ClassDiagramBuilderCreatorFactory();

                var diagramBuilder = classDiagramBuilderCreator.FactoryMethod("ClassDiagram");
                IParseTree tree = parser.translationUnit();

                if (parser.NumberOfSyntaxErrors == 0)
                {
                    CppClassDiagramVisitor classDiagramVisitor = new CppClassDiagramVisitor(diagramBuilder);
                    Console.WriteLine(classDiagramVisitor.Visit(tree));
                }

                var xmi = diagramBuilder.BuildDiagram();
                byte[] xmiBytes = Encoding.UTF8.GetBytes(xmi);
                var xmiStream = new MemoryStream(xmiBytes);
                
                return File(xmiStream, "application/xml", "class_diagram.xmi");
            }
            else
            {
                return BadRequest("Invalid token");
            }
        }

        #endregion
        
        #region SourceCode
        [HttpPost("code", Name = "UploadCode")]
        public IActionResult UploadCode(UploadCodeModel model)
        {
            if (TokenManager.VerifyAndRevalidateToken(model.Token, tokenValidityMinutes))
            {
                var user = TokenManager.GetUser(model.Token, tokenValidityMinutes);
                using var db = new DatabaseContext();

                //string to base 64 string 
                //string codeBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(model.Code));
                var sourceCode = new SourceCode
                {
                    Code = model.Code,
                    UserId = user.Id
                };
                db.SourceCodes.Add(sourceCode);
                db.SaveChanges();
                return Ok($"Code uploaded, id: {sourceCode.Id}");
            }
            else
            {
                return BadRequest("Invalid token");
            }
        }
        
        [HttpPost("code-file", Name = "UploadCodeFile")]
        public IActionResult UploadCodeFile(string token, IFormFile file)
        {
            if (TokenManager.VerifyAndRevalidateToken(token, tokenValidityMinutes))
            {
                var user = TokenManager.GetUser(token, tokenValidityMinutes);
                using var db = new DatabaseContext();

                using var ms = new MemoryStream();
                file.CopyTo(ms);
                var fileBytes = ms.ToArray();
                string code = System.Text.Encoding.UTF8.GetString(fileBytes);
                var base64code = Convert.ToBase64String(fileBytes);
                var sourceCode = new SourceCode
                {
                    Code = base64code,
                    UserId = user.Id
                };
                db.SourceCodes.Add(sourceCode);
                db.SaveChanges();
                return Ok($"Code uploaded, id: {sourceCode.Id}");
            }
            else
            {
                return BadRequest("Invalid token");
            }
        }
        
        [HttpDelete("code", Name = "RemoveCode")]
        public IActionResult RemoveCode(RemoveCodeModel model)
        {
            if (TokenManager.VerifyAndRevalidateToken(model.Token, tokenValidityMinutes))
            {
                var user = TokenManager.GetUser(model.Token, tokenValidityMinutes);
                using var db = new DatabaseContext();

                var sourceCode = db.SourceCodes.FirstOrDefault(s => s.Id == model.Id && s.UserId == user.Id);
                if (sourceCode == null)
                {
                    return NotFound("Code does not exist");
                }

                db.SourceCodes.Remove(sourceCode);
                db.SaveChanges();
                return Ok("Code removed");
            }
            else
            {
                return BadRequest("Invalid token");
            }
        }

        [HttpGet("code", Name = "ListCode")]
        public IActionResult ListCode([Required(ErrorMessage = "Token is required")]
            [RegularExpression(@"^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$", ErrorMessage = "Invalid GUID format for Token")]
            string token,
            
            [Required(ErrorMessage = "Id is required")]
            [Range(1, int.MaxValue, ErrorMessage = "Id must be greater than 0")]
            int id)
        {
            if (TokenManager.VerifyAndRevalidateToken(token, tokenValidityMinutes))
            {
                var user = TokenManager.GetUser(token, tokenValidityMinutes);
                using var db = new DatabaseContext();

                var sourceCodes = db.SourceCodes.Where(s =>
                    s.UserId == user.Id &&
                    s.Id == id)
                    .ToList();

                if (sourceCodes.Count == 0)
                {
                    return Ok(new List<SourceCodeExt>());
                }

                List<SourceCodeExt> sourceCodeExts = new List<SourceCodeExt>();
                foreach (var sourceCode in sourceCodes)
                {
                    sourceCodeExts.Add(new SourceCodeExt
                    {
                        Id = sourceCode.Id,
                        Code = sourceCode.Code
                    });
                }
                return Ok(sourceCodeExts);
            }
            else
            {
                return BadRequest("Invalid token");
            }
        }
        
        [HttpGet("code-all", Name = "ListAllCode")]
        public IActionResult ListAllCode([RegularExpression(@"^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$", ErrorMessage = "Invalid GUID format for Token")]
            string token)
        {
            if (TokenManager.VerifyAndRevalidateToken(token, tokenValidityMinutes))
            {
                var user = TokenManager.GetUser(token, tokenValidityMinutes);
                using var db = new DatabaseContext();

                var sourceCodes = db.SourceCodes.Where(s =>
                    s.UserId == user.Id)
                    .ToList();

                if (sourceCodes.Count == 0)
                {
                    return Ok(new List<SourceCodeExt>());
                }

                List<SourceCodeExt> sourceCodeExts = new List<SourceCodeExt>();
                foreach (var sourceCode in sourceCodes)
                {
                    sourceCodeExts.Add(new SourceCodeExt
                    {
                        Id = sourceCode.Id,
                        Code = sourceCode.Code
                    });
                }
                return Ok(sourceCodeExts);
            }
            else
            {
                return BadRequest("Invalid token");
            }
        }
        #endregion

        #region User
        [HttpPost("authenticate-user-password", Name = "AuthenticateUserPassword")]
        public IActionResult AuthenticateUserPassword(AuthenticateUserModel model)
        {
            using var db = new DatabaseContext();

            var user = db.Users.FirstOrDefault(u => u.Name == model.Username);
            if (user == null)
            {
                return NotFound("User does not exist");
            }

            if (PasswordHasher.Verify(model.Password, user.PasswordHash))
            {
                TokenManager.AssignToken(user);
                return Ok($"User authenticated, token: {user.Token}");
            }
            else
            {
                return BadRequest("User not authenticated");
            }
        }

        [HttpPost("register-user", Name = "RegisterUser")]
        public IActionResult RegisterUser(RegisterUserModel model)
        {
            if (TokenManager.VerifyAndRevalidateToken(model.Token, tokenValidityMinutes))
            {
                var passwordHash = PasswordHasher.Hash(model.Password);

                using var db = new DatabaseContext();
                var user = db.Users.FirstOrDefault(u => u.Name == passwordHash);
                if (user != null)
                {
                    //user exists
                    return BadRequest("User already exists");
                }

                db.Users.Add(new User
                {
                    Name = model.Username,
                    PasswordHash = passwordHash,
                    Token = "NOT LOGGED IN YET",
                    LastAccess = DateTime.MinValue
                });
                db.SaveChanges();
                return Ok("User created");
            }
            else
            {
                return BadRequest("Invalid token");
            }
        }
        #endregion

    }
}