diff --git a/ControllersLogic/ControllersLogic.csproj b/ControllersLogic/ControllersLogic.csproj
new file mode 100644
index 0000000..83cfc9a
--- /dev/null
+++ b/ControllersLogic/ControllersLogic.csproj
@@ -0,0 +1,82 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {89FC8B31-B22A-40DD-981E-89A86D96BE34}
+ Library
+ Properties
+ ControllersLogic
+ ControllersLogic
+ v4.5.2
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\CloudinaryDotNet.1.0.24\lib\net40\CloudinaryDotNet.dll
+ True
+
+
+ ..\packages\MongoDB.Bson.2.2.4\lib\net45\MongoDB.Bson.dll
+ True
+
+
+ ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {4841ab80-72fd-4f53-bb68-28b111786669}
+ DB
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ControllersLogic/Interfaces/ICommentLogic.cs b/ControllersLogic/Interfaces/ICommentLogic.cs
new file mode 100644
index 0000000..3dffc66
--- /dev/null
+++ b/ControllersLogic/Interfaces/ICommentLogic.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using DB.Models;
+
+namespace ControllersLogic.Interfaces
+{
+ public interface ICommentLogic
+ {
+ String AddComment(String text, String name);
+ List GetAllComment();
+ CommentWithoutObjectId GetById(String id);
+ CommentWithoutObjectId UpdateById(String id, String name, String text);
+ }
+}
diff --git a/ControllersLogic/Interfaces/IImageLogic.cs b/ControllersLogic/Interfaces/IImageLogic.cs
new file mode 100644
index 0000000..8cee069
--- /dev/null
+++ b/ControllersLogic/Interfaces/IImageLogic.cs
@@ -0,0 +1,23 @@
+using DB.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+
+namespace ControllersLogic.Interfaces
+{
+ public interface IImageLogic
+ {
+ String AddCommentToImage(String simageId, String text, String name);
+ Task AddImage(String url, String name);
+ String DeleteCommentFromImage(String idImage, String idComment);
+ String DownloadImage(HttpPostedFileBase uploadImage);
+ List GetAllImage();
+ ImageWithoutObjectId GetById(String id);
+ ImageWithoutObjectId GetByIdAndVersion(String id, int version);
+ List GetCommentFromImage(String simageId);
+ ImageWithoutObjectId UpdateById(String id, String name, String url);
+ }
+}
diff --git a/ControllersLogic/Interfaces/IProjectLogic.cs b/ControllersLogic/Interfaces/IProjectLogic.cs
new file mode 100644
index 0000000..51f2b30
--- /dev/null
+++ b/ControllersLogic/Interfaces/IProjectLogic.cs
@@ -0,0 +1,23 @@
+using DB.Models;
+using System;
+using System.Collections.Generic;
+
+namespace ControllersLogic.Interfaces
+{
+ public interface IProjectLogic
+ {
+ String AddCommentToProject(String text, String name, String sprojectId);
+ String AddImageToProject(String url, String name, String sprojectId);
+ String AddProject(String name);
+ String AddProjectToProject(String sidRoot, String name);
+ String DeleteById(String id);
+ String DeleteCommentFromProject(String projectId, String commentId);
+ String DeleteImageFromProject(String projectId, String imageId);
+ List GetAllProjects();
+ ProjectWithoutObjectId GetById(String id);
+ List GetCommentsFormProject(String sidRoot);
+ List GetImagesFormProject(String sidRoot);
+ List GetPtojectsFormProject(String sidRoot);
+ ProjectWithoutObjectId UpdateById(String id, String name);
+ }
+}
diff --git a/ControllersLogic/Logic/CommentLogic.cs b/ControllersLogic/Logic/CommentLogic.cs
new file mode 100644
index 0000000..095f94a
--- /dev/null
+++ b/ControllersLogic/Logic/CommentLogic.cs
@@ -0,0 +1,73 @@
+using ControllersLogic.Interfaces;
+using DB.Interfaces;
+using DB.Models;
+using MongoDB.Bson;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ControllerLogic
+{
+ public class CommentLogic: ICommentLogic
+ {
+ private static ICommentRepository _commentRepository = new DB.Repositories.DBCommentRepository();
+ public String AddComment(String text, String name)
+ {
+ Comment comment = new Comment();
+ comment.Text = text;
+ comment.CreationelData = DateTime.UtcNow;
+ comment.Name = name;
+ comment.Version = 1;
+ var id = _commentRepository.AddComment(comment).Id.ToString();
+ return id;
+ }
+ public List GetAllComment()
+ {
+ var comments = CommentWithoutObjectId.CommentsToCommentWithoutObjectId(_commentRepository.GetAll());
+ return comments;
+ }
+ public CommentWithoutObjectId GetById(String id)
+ {
+ var objectId = new ObjectId();
+ if (!ObjectId.TryParse(id, out objectId))
+ {
+ throw new Exception("This isn't objectId");
+ }
+ if (objectId == null)
+ {
+ throw new Exception("Bad id");
+ }
+ var comment = CommentWithoutObjectId.CommentToCommentWithoutObjectId(_commentRepository.GetCommentById(objectId));
+ if (comment == null)
+ {
+ throw new Exception("Bad id");
+ }
+ return comment;
+ }
+ public CommentWithoutObjectId UpdateById(String id, String name, String text)
+ {
+ var objectId = new ObjectId();
+ if (!ObjectId.TryParse(id, out objectId))
+ {
+ throw new Exception("This isn't objectId");
+ }
+ if (objectId == null)
+ {
+ throw new Exception("Bad id");
+ }
+ var comment = _commentRepository.GetCommentById(objectId);
+ if (comment == null)
+ {
+ throw new Exception("Bad id");
+ }
+ comment.Name = name;
+ comment.Text = text;
+ comment.Version++;
+ _commentRepository.DeleteById(objectId);
+ _commentRepository.AddComment(comment);
+ return CommentWithoutObjectId.CommentToCommentWithoutObjectId(comment);
+ }
+ }
+}
diff --git a/ControllersLogic/Logic/ImageLogic.cs b/ControllersLogic/Logic/ImageLogic.cs
new file mode 100644
index 0000000..650e0d1
--- /dev/null
+++ b/ControllersLogic/Logic/ImageLogic.cs
@@ -0,0 +1,176 @@
+using ControllersLogic.Interfaces;
+using DB.Interfaces;
+using DB.Models;
+using MongoDB.Bson;
+using System;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.IO;
+using System.Web;
+
+namespace ControllersLogic.Logic
+{
+ public class ImageLogic : IImageLogic
+ {
+ private IImageRepository _imageRepository = new DB.Repositories.DBImageRepository();
+ private ICommentRepository _commentRepository = new DB.Repositories.DBCommentRepository();
+
+ public String AddCommentToImage(String simageId, String text, String name)
+ {
+ Comment comment = new Comment();
+ comment.Text = text;
+ comment.CreationelData = DateTime.UtcNow;
+ comment.Name = name;
+ comment.Version = 1;
+ var commentId = _commentRepository.AddComment(comment).Id;
+ var imageId = new ObjectId();
+ if (!ObjectId.TryParse(simageId, out imageId))
+ {
+ return "Bad id it's not objectId";
+ }
+ if (_imageRepository.GetImageById(imageId) == null)
+ {
+ return "Bad id image";
+ }
+ _imageRepository.AddCommentToImage(commentId, imageId);
+ return commentId.ToString();
+ }
+ public async Task AddImage(String url, String name)
+ {
+ Image image = new Image();
+ image.Url = url;
+ image.Version = 1;
+ image.Name = name;
+ image.CreationelData = DateTime.UtcNow;
+ var id = _imageRepository.AddImage(image).Id;
+ image.StartId = id.ToString();
+ await _imageRepository.DeleteByIdAsync(id);
+ _imageRepository.AddImage(image);
+ return id.ToString();
+ }
+ public String DeleteCommentFromImage(String idImage, String idComment)
+ {
+ var objectIdImage = new ObjectId();
+ var objectIdComment = new ObjectId();
+ if (!ObjectId.TryParse(idImage, out objectIdImage))
+ {
+ return "Bad image id";
+ }
+ if (!ObjectId.TryParse(idComment, out objectIdComment))
+ {
+ return "Bad comment id";
+ }
+ if (_imageRepository.GetImageById(objectIdImage) == null)
+ {
+ return "Bad image id, not found in DB";
+ }
+ _imageRepository.DeleteCommentFromImage(objectIdImage, objectIdComment);
+ _commentRepository.DeleteById(objectIdComment);
+ return "OK";
+ }
+ public String DownloadImage(HttpPostedFileBase uploadImage)
+ {
+ if (uploadImage != null)
+ {
+ string fileName = Path.GetFileName(uploadImage.FileName);
+ var input = uploadImage.InputStream;
+ CloudinaryDotNet.Account account = new CloudinaryDotNet.Account("hzvwvtbls", "482455376217895", "bXPz-CiQrEjZp4xqSV8UK_nfI2c");
+ CloudinaryDotNet.Cloudinary cloudinary = new CloudinaryDotNet.Cloudinary(account);
+ CloudinaryDotNet.Actions.ImageUploadParams uploadParams = new CloudinaryDotNet.Actions.ImageUploadParams()
+ {
+ File = new CloudinaryDotNet.Actions.FileDescription(fileName, input)
+ };
+ CloudinaryDotNet.Actions.ImageUploadResult uploadResult = cloudinary.Upload(uploadParams);
+ string url = cloudinary.Api.UrlImgUp.BuildUrl(String.Format("{0}.{1}", uploadResult.PublicId, uploadResult.Format));
+ return url;
+ }
+ else
+ {
+ return "Bad file";
+ }
+ }
+ public List GetAllImage()
+ {
+ var images = ImageWithoutObjectId.ImagesToImageWithoutObjectId(_imageRepository.GetAllImage());
+ return images;
+ }
+ public ImageWithoutObjectId GetById(String id)
+ {
+ var objectId = new ObjectId();
+ if (!ObjectId.TryParse(id, out objectId))
+ {
+ throw new Exception("Bad id it's not objectId");
+ }
+ if (objectId == null)
+ {
+ throw new Exception("Bad id");
+ }
+ var image = ImageWithoutObjectId.ImageToImageWithoutObjectId(_imageRepository.GetImageById(objectId));
+ if (image == null)
+ {
+ throw new Exception("Bad id");
+ }
+ return image;
+ }
+ public ImageWithoutObjectId GetByIdAndVersion(String id, int version)
+ {
+ var objectId = new ObjectId();
+ if (!ObjectId.TryParse(id, out objectId))
+ {
+ throw new Exception("Bad id it's not objectId");
+ }
+ if (objectId == null)
+ {
+ throw new Exception("Bad id");
+ }
+ var image = ImageWithoutObjectId.ImageToImageWithoutObjectId(_imageRepository.GetImageByIdAndVersion(objectId, version));
+ if (image == null)
+ {
+ throw new Exception("Bad id");
+ }
+ return image;
+ }
+ public List GetCommentFromImage(String simageId)
+ {
+ var imageId = new ObjectId();
+ if (!ObjectId.TryParse(simageId, out imageId))
+ {
+ throw new Exception("Bad id it's not objectId");
+ }
+ var image = _imageRepository.GetImageById(imageId);
+ if (image == null)
+ {
+ throw new Exception("Bad id image, this image don't found in DB");
+ }
+ var comments = CommentWithoutObjectId.CommentsToCommentWithoutObjectId(_commentRepository.GetCommentsByIds(image.Comments));
+ return comments;
+ }
+ public ImageWithoutObjectId UpdateById(String id, String name, String url)
+ {
+ var objectId = new ObjectId();
+ if (!ObjectId.TryParse(id, out objectId))
+ {
+ throw new Exception("Bad id it's not objectId");
+ }
+ if (objectId == null)
+ {
+ throw new Exception("Bad id");
+ }
+ var image = _imageRepository.GetImageById(objectId);
+ Image prev_image = _imageRepository.GetImageById(objectId);
+ if (image == null)
+ {
+ throw new Exception("Bad id");
+ }
+ image.Url = url;
+ image.Version++;
+ image.Name = name;
+ image.StartId = id;
+ _imageRepository.UpdateImage(objectId, name, url, image.Version);
+ prev_image.Id = new ObjectId();
+ prev_image.StartId = id;
+ _imageRepository.AddImage(prev_image);
+ return ImageWithoutObjectId.ImageToImageWithoutObjectId(image);
+ }
+ }
+}
diff --git a/ControllersLogic/Logic/ProjectLogic.cs b/ControllersLogic/Logic/ProjectLogic.cs
new file mode 100644
index 0000000..07f53d5
--- /dev/null
+++ b/ControllersLogic/Logic/ProjectLogic.cs
@@ -0,0 +1,239 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ControllersLogic.Interfaces;
+using DB.Interfaces;
+using DB.Repositories;
+using DB.Models;
+using MongoDB.Bson;
+
+namespace ControllersLogic.Logic
+{
+ public class ProjectLogic : IProjectLogic
+ {
+ private IProjectRepository _projectRepository = new DBProjectRepository();
+ private IImageRepository _imageRepository = new DBImageRepository();
+ private ICommentRepository _commentRepository = new DBCommentRepository();
+
+ public String AddCommentToProject(String text, String name, String sprojectId)
+ {
+ var projectId = new ObjectId();
+ if (!ObjectId.TryParse(sprojectId, out projectId))
+ {
+ return "Bad id it's not objectId";
+ }
+ if (_projectRepository.GetProjectById(projectId) == null)
+ {
+ return "Bad id it's not found in DB" ;
+ }
+ Comment comment = new Comment();
+ comment.Text = text;
+ comment.CreationelData = DateTime.UtcNow;
+ comment.Name = name;
+ comment.Version = 1;
+ var commentId = _commentRepository.AddComment(comment).Id;
+ _projectRepository.AddCommentToProject(commentId, projectId);
+ return commentId.ToString();
+ }
+ public String AddImageToProject(String url, String name, String sprojectId)
+ {
+ var projectId = new ObjectId();
+ if (!ObjectId.TryParse(sprojectId, out projectId))
+ {
+ return "Bad id it's not objectId";
+ }
+ if (_projectRepository.GetProjectById(projectId) == null)
+ {
+ return "Bad id it's not found in DB";
+ }
+ Image image = new Image();
+ image.Url = url;
+ image.Version = 1;
+ image.Name = name;
+ image.CreationelData = DateTime.UtcNow;
+ var imageId = _imageRepository.AddImage(image).Id;
+ image.StartId = imageId.ToString();
+ _imageRepository.DeleteByIdAsync(imageId);
+ _imageRepository.AddImage(image);
+ _projectRepository.AddImageToProject(imageId, projectId);
+ return imageId.ToString();
+ }
+ public String AddProject(String name)
+ {
+ Project project = new Project();
+ project.Name = name;
+ project.Version = 1;
+ project.CreationelData = DateTime.UtcNow;
+ var id = _projectRepository.AddProject(project).Id;
+ return id.ToString();
+ }
+ public String AddProjectToProject(String sidRoot, String name)
+ {
+ var idRoot = new ObjectId();
+ if (!ObjectId.TryParse(sidRoot, out idRoot))
+ {
+ return "Bad id it's not objectId";
+ }
+ if (_projectRepository.GetProjectById(idRoot) == null)
+ {
+ return "Bad id it's not found in DB" ;
+ }
+ Project project = new Project();
+ project.Name = name;
+ project.Version = 1;
+ project.CreationelData = DateTime.UtcNow;
+ var idNew = _projectRepository.AddProject(project).Id;
+ _projectRepository.AddProjectToProject(idNew, idRoot);
+ return idNew.ToString();
+ }
+ public String DeleteById(String id)
+ {
+ var objectId = new ObjectId();
+ if (!ObjectId.TryParse(id, out objectId))
+ {
+ return "Bad id it's not objectId" ;
+ }
+ if (objectId == null)
+ {
+ return "Bad id";
+ }
+ if (_projectRepository.GetProjectById(objectId) == null)
+ {
+ return "Bad id";
+ }
+ _projectRepository.DeleteById(objectId);
+ return "OK";
+ }
+ public String DeleteCommentFromProject(String projectId, String commentId)
+ {
+ var objectIdProject = new ObjectId();
+ var objectIdComment = new ObjectId();
+ if (!ObjectId.TryParse(projectId, out objectIdProject))
+ {
+ return "Bad id project it's not objectId";
+ }
+ if (!ObjectId.TryParse(commentId, out objectIdComment))
+ {
+ return "Bad id coomment it's not objectId";
+ }
+ if (_projectRepository.GetProjectById(objectIdProject) == null)
+ {
+ return "Bad id";
+ }
+ _projectRepository.DeleteCommentFromProject(objectIdProject, objectIdComment);
+ _commentRepository.DeleteById(objectIdComment);
+ return "OK";
+ }
+ public String DeleteImageFromProject(String projectId, String imageId)
+ {
+ var objectIdProject = new ObjectId();
+ var objectIdImage = new ObjectId();
+ if (!ObjectId.TryParse(projectId, out objectIdProject))
+ {
+ return "Bad id project it's not objectId";
+ }
+ if (!ObjectId.TryParse(imageId, out objectIdImage))
+ {
+ return "Bad id image it's not objectId";
+ }
+ if (_projectRepository.GetProjectById(objectIdProject) == null)
+ {
+ return "Bad id";
+ }
+ _projectRepository.DeleteImageFromProject(objectIdProject, objectIdImage);
+ _imageRepository.DeleteById(objectIdImage);
+ return "OK";
+ }
+ public List GetAllProjects()
+ {
+ var projects = ProjectWithoutObjectId.ProjectsToProjectWithoutObjectId(_projectRepository.GetAllProject());
+ return projects;
+ }
+ public ProjectWithoutObjectId GetById(String id)
+ {
+ var objectId = new ObjectId();
+ if (!ObjectId.TryParse(id, out objectId))
+ {
+ throw new Exception("Bad id it's not objectId");
+ }
+ if (objectId == null)
+ {
+ throw new Exception("Bad id");
+ }
+ var project = ProjectWithoutObjectId.ProjectToProjectWithoutObjectId(_projectRepository.GetProjectById(objectId));
+ if (project == null)
+ {
+ throw new Exception("Bad id");
+ }
+ return project;
+ }
+ public List GetCommentsFormProject(String sidRoot)
+ {
+ var idRoot = new ObjectId();
+ if (!ObjectId.TryParse(sidRoot, out idRoot))
+ {
+ throw new Exception("Bad id it's not objectId");
+ }
+ var project = _projectRepository.GetProjectById(idRoot);
+ if (project == null)
+ {
+ throw new Exception("Bad id it's not found in DB");
+ }
+ var comments = CommentWithoutObjectId.CommentsToCommentWithoutObjectId(_commentRepository.GetCommentsByIds(project.Comments));
+ return comments;
+ }
+ public List GetImagesFormProject(String sidRoot)
+ {
+ var idRoot = new ObjectId();
+ if (!ObjectId.TryParse(sidRoot, out idRoot))
+ {
+ throw new Exception("Bad id it's not objectId" );
+ }
+ var project = _projectRepository.GetProjectById(idRoot);
+ if (project == null)
+ {
+ throw new Exception("Bad id it's not found in DB");
+ }
+ var images = ImageWithoutObjectId.ImagesToImageWithoutObjectId(_imageRepository.GetImagesByIds(project.Images));
+ return images;
+ }
+ public List GetPtojectsFormProject(String sidRoot)
+ {
+ var idRoot = new ObjectId();
+ if (!ObjectId.TryParse(sidRoot, out idRoot))
+ {
+ throw new Exception("Bad id it's not objectId");
+ }
+ var project = _projectRepository.GetProjectById(idRoot);
+ if (project == null)
+ {
+ throw new Exception("Bad id it's not found in DB");
+ }
+ var projects = ProjectWithoutObjectId.ProjectsToProjectWithoutObjectId(_projectRepository.GetProjectsByIds(project.Projects));
+ return projects;
+ }
+ public ProjectWithoutObjectId UpdateById(String id, String name)
+ {
+ var objectId = new ObjectId();
+ if (!ObjectId.TryParse(id, out objectId))
+ {
+ throw new Exception("Bad id it's not objectId");
+ }
+ if (objectId == null)
+ {
+ throw new Exception("Bad id");
+ }
+ var project = _projectRepository.GetProjectById(objectId);
+ if (project == null)
+ {
+ throw new Exception("Bad id");
+ }
+ project.Name = name;
+ project.Version++;
+ _projectRepository.UpdateProject(objectId, name, project.Version);
+ return ProjectWithoutObjectId.ProjectToProjectWithoutObjectId(project);
+ }
+ }
+}
diff --git a/ControllersLogic/Properties/AssemblyInfo.cs b/ControllersLogic/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..6f17cac
--- /dev/null
+++ b/ControllersLogic/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ControllersLogic")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ControllersLogic")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("89fc8b31-b22a-40dd-981e-89a86d96be34")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/ControllersLogic/packages.config b/ControllersLogic/packages.config
new file mode 100644
index 0000000..a6059cc
--- /dev/null
+++ b/ControllersLogic/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DB/Interfaces/IImageRepository.cs b/DB/Interfaces/IImageRepository.cs
index 0c1538f..e016748 100644
--- a/DB/Interfaces/IImageRepository.cs
+++ b/DB/Interfaces/IImageRepository.cs
@@ -11,12 +11,15 @@ namespace DB.Interfaces
public interface IImageRepository
{
void DeleteById(ObjectId id);
+ Task DeleteByIdAsync(ObjectId id);
void DeleteAll();
void DeleteCommentFromImage(ObjectId imageId, ObjectId commentId);
Image AddImage(Image image);
Image GetImageById(ObjectId id);
+ Image GetImageByIdAndVersion(ObjectId id, int version);
List GetAllImage();
void AddCommentToImage(ObjectId newComments, ObjectId idImage);
List GetImagesByIds(List ids);
+ void UpdateImage(ObjectId id, String name, String url, uint version);
}
}
diff --git a/DB/Interfaces/IProjectRepository.cs b/DB/Interfaces/IProjectRepository.cs
index 06b2026..0b55621 100644
--- a/DB/Interfaces/IProjectRepository.cs
+++ b/DB/Interfaces/IProjectRepository.cs
@@ -13,6 +13,7 @@ public interface IProjectRepository
void DeleteById(ObjectId id);
void DeleteAll();
void DeleteCommentFromProject(ObjectId projectId, ObjectId commentId);
+ void DeleteProjectFromProject(ObjectId rootProjectId, ObjectId deletedProjectId);
void DeleteImageFromProject(ObjectId projectId, ObjectId imageId);
Project AddProject(Project project);
Project GetProjectById(ObjectId id);
@@ -21,5 +22,6 @@ public interface IProjectRepository
void AddImageToProject(ObjectId newImages, ObjectId iDProject);
void AddCommentToProject(ObjectId newComments, ObjectId iDProject);
List GetProjectsByIds(List ids);
+ void UpdateProject(ObjectId id, String name, uint version);
}
}
diff --git a/DB/Models/BaseEntity.cs b/DB/Models/BaseEntity.cs
index 7c2b5fb..bdadcc3 100644
--- a/DB/Models/BaseEntity.cs
+++ b/DB/Models/BaseEntity.cs
@@ -13,6 +13,7 @@ public class BaseEntity
public ObjectId Author { get; set; }
public uint Version { get; set; }
public String Name { get; set; }
+ public DateTime LastModified { get; set; }
public DateTime CreationelData { get; set; }
}
}
diff --git a/DB/Models/Image.cs b/DB/Models/Image.cs
index 4550518..781c1f1 100644
--- a/DB/Models/Image.cs
+++ b/DB/Models/Image.cs
@@ -11,6 +11,7 @@ public class Image:BaseEntity
{
public List Comments { get; set; }
public String Url { get; set; }
+ public String StartId { get; set; }
public Image()
{
this.Comments = new List();
@@ -36,7 +37,7 @@ public static ImageWithoutObjectId ImageToImageWithoutObjectId(Image image)
Url = image.Url,
Version = image.Version,
Name = image.Name,
- Id = image.Id.ToString(),
+ Id = image.StartId,
CreationelData = image.CreationelData,
Comments = new List()};
if (image.Comments != null)
diff --git a/DB/MongoClientFactory.cs b/DB/MongoClientFactory.cs
index 6e17037..a25c8e3 100644
--- a/DB/MongoClientFactory.cs
+++ b/DB/MongoClientFactory.cs
@@ -12,21 +12,18 @@ namespace DB
{
public class MongoClientFactory
{
- public static MongoUrl GetMongoUrl()
- {
- return new MongoUrl(ConfigurationManager.AppSettings.Get("MONGOLAB_URI"));
- }
- public static MongoServer GetMongoServer()
- {
- var client = new MongoClient(GetMongoUrl());
- var server = client.GetServer();
- return server;
-
+ public static MongoClient GetMongoClient()
+ {
+ var dbConnectionString = System.Configuration.ConfigurationManager.AppSettings["DB"];
+ if (!String.IsNullOrEmpty(dbConnectionString))
+ {
+ return new MongoClient(dbConnectionString);
+ }
+ return new MongoClient();
}
- public static MongoDatabase GetMongoDatabase()
+ public static IMongoDatabase GetMongoDatabase(String name = "mongodb")
{
- var database = GetMongoServer().GetDatabase(GetMongoUrl().DatabaseName);
- return database;
+ return GetMongoClient().GetDatabase(name);
}
}
}
diff --git a/DB/Repositories/DBCommentRepository.cs b/DB/Repositories/DBCommentRepository.cs
index c9e16e7..c4f9db6 100644
--- a/DB/Repositories/DBCommentRepository.cs
+++ b/DB/Repositories/DBCommentRepository.cs
@@ -14,7 +14,7 @@ namespace DB.Repositories
{
public class DBCommentRepository : ICommentRepository
{
- private readonly MongoCollection _commentCollection;
+ private readonly IMongoCollection _commentCollection;
public DBCommentRepository()
{
@@ -23,21 +23,21 @@ public DBCommentRepository()
}
public void DeleteById(ObjectId id)
{
- _commentCollection.Remove(Query.EQ("_id", id));
+ _commentCollection.DeleteOne(c => c.Id.Equals(id));
}
public void DeleteAll()
{
- _commentCollection.RemoveAll();
+ _commentCollection.DeleteMany(c => true);
}
public Comment AddComment(Comment comment)
{
- _commentCollection.Insert(comment);
+ _commentCollection.InsertOne(comment);
return comment;
}
public List GetAll()
{
- return _commentCollection.FindAll().ToList();
+ return _commentCollection.AsQueryable().ToList();
}
public Comment GetCommentById(ObjectId id)
@@ -47,7 +47,7 @@ public Comment GetCommentById(ObjectId id)
}
public List GetCommentsByIds(List ids)
{
- return _commentCollection.FindAll().Where(item => ids.Contains(item.Id)).ToList();
+ return _commentCollection.AsQueryable().Where(item => ids.Contains(item.Id)).ToList();
}
}
}
diff --git a/DB/Repositories/DBImageRepository.cs b/DB/Repositories/DBImageRepository.cs
index 81d7fc2..60bd270 100644
--- a/DB/Repositories/DBImageRepository.cs
+++ b/DB/Repositories/DBImageRepository.cs
@@ -14,7 +14,7 @@ namespace DB.Repositories
{
public class DBImageRepository : IImageRepository
{
- private readonly MongoCollection _imageCollection;
+ private readonly IMongoCollection _imageCollection;
public DBImageRepository()
{
@@ -23,30 +23,36 @@ public DBImageRepository()
}
public void DeleteAll()
{
- _imageCollection.RemoveAll();
+ _imageCollection.DeleteMany(prop => true);
}
public void DeleteById(ObjectId id)
{
- _imageCollection.Remove(Query.EQ("_id", id));
+ _imageCollection.DeleteOne(prop => prop.Id.Equals(id));
+ }
+ public async Task DeleteByIdAsync(ObjectId id)
+ {
+ await _imageCollection.DeleteOneAsync(prop => prop.Id.Equals(id));
}
public void DeleteCommentFromImage(ObjectId imageId, ObjectId commentId)
{
- var image = _imageCollection.AsQueryable().FirstOrDefault(i => i.Id.Equals(imageId));
- _imageCollection.Remove(Query.EQ("_id", imageId));
- image.Comments.Remove(commentId);
- _imageCollection.Insert(image);
+ var list = _imageCollection.AsQueryable().FirstOrDefault(p => p.Id.Equals(imageId)).Comments;
+ list.Remove(commentId);
+ var update = Builders.Update
+ .Set("Comments", list)
+ .CurrentDate("LastModified");
+ var result = _imageCollection.UpdateOne(prop => prop.Id.Equals(imageId), update);
}
public void AddCommentToImage(ObjectId newComments, ObjectId idImage)
{
- var image = _imageCollection.AsQueryable().FirstOrDefault(im => im.Id.Equals(idImage));
- _imageCollection.Remove(Query.EQ("Id", idImage));
- image.Comments.Add(newComments);
- _imageCollection.Insert(image);
+ var update = Builders.Update
+ .AddToSet("Comments", newComments)
+ .CurrentDate("LastModified");
+ var result = _imageCollection.UpdateOne(p => p.Id.Equals(idImage), update);
}
public Image AddImage(Image image)
{
- _imageCollection.Insert(image);
+ _imageCollection.InsertOne(image);
return image;
}
@@ -60,23 +66,24 @@ public Image GetImageById(ObjectId id)
{
return _imageCollection.AsQueryable().FirstOrDefault(im => im.Id.Equals(id));
}
+ public Image GetImageByIdAndVersion(ObjectId id, int version)
+ {
+ return _imageCollection.AsQueryable().FirstOrDefault(im => im.StartId.Equals(id) && im.Version.Equals(version));
+ }
+
public List GetImagesByIds(List ids)
{
- var list = _imageCollection.FindAll().ToList();
- HashSet id = new HashSet();
- foreach (ObjectId i in ids)
- {
- id.Add(i);
- }
- var images = new List();
- foreach (Image i in list)
- {
- if (id.Contains(i.Id))
- {
- images.Add(i);
- }
- }
- return images;
+ return _imageCollection.AsQueryable().Where(i => ids.Contains(i.Id)).ToList();
+ }
+
+ public void UpdateImage(ObjectId id, string name, string url, uint version)
+ {
+ var update = Builders.Update
+ .Set("Version", version)
+ .Set("Name", name)
+ .Set("Url", url)
+ .CurrentDate("LastModified");
+ var result = _imageCollection.UpdateOne(p => p.Id.Equals(id), update);
}
}
}
diff --git a/DB/Repositories/DBProjectRepository.cs b/DB/Repositories/DBProjectRepository.cs
index b144120..fdff136 100644
--- a/DB/Repositories/DBProjectRepository.cs
+++ b/DB/Repositories/DBProjectRepository.cs
@@ -14,7 +14,7 @@ namespace DB.Repositories
{
public class DBProjectRepository : IProjectRepository
{
- private readonly MongoCollection _projectCollection;
+ private readonly IMongoCollection _projectCollection;
public DBProjectRepository()
{
@@ -23,37 +23,50 @@ public DBProjectRepository()
}
public void DeleteById(ObjectId id)
{
- _projectCollection.Remove(Query.EQ("_id", id));
+ _projectCollection.DeleteOne(p => p.Id.Equals(id));
}
public void DeleteAll()
{
- _projectCollection.RemoveAll();
+ _projectCollection.DeleteMany(p => true);
}
public void DeleteCommentFromProject(ObjectId projectId, ObjectId commentId)
{
- var project = _projectCollection.AsQueryable().FirstOrDefault(p=>p.Id.Equals(projectId));
- _projectCollection.Remove(Query.EQ("_id", projectId));
- project.Comments.Remove(commentId);
- _projectCollection.Insert(project);
+ var list = _projectCollection.AsQueryable().FirstOrDefault(p => p.Id.Equals(projectId)).Comments;
+ list.Remove(commentId);
+ var update = Builders.Update
+ .Set("Comments", list)
+ .CurrentDate("LastModified");
+ var result = _projectCollection.UpdateOne(p => p.Id.Equals(projectId), update);
+ }
+ public void DeleteProjectFromProject(ObjectId rootProjectId, ObjectId deletedProjectId)
+ {
+ var list = _projectCollection.AsQueryable().FirstOrDefault(p => p.Id.Equals(rootProjectId)).Projects;
+ list.Remove(deletedProjectId);
+ var update = Builders.Update
+ .Set("Projects", list)
+ .CurrentDate("LastModified");
+ var result = _projectCollection.UpdateOne(p => p.Id.Equals(rootProjectId), update);
}
public void DeleteImageFromProject(ObjectId projectId, ObjectId imageId)
{
- var project = _projectCollection.AsQueryable().FirstOrDefault(p => p.Id.Equals(projectId));
- _projectCollection.Remove(Query.EQ("_id", projectId));
- project.Images.Remove(imageId);
- _projectCollection.Insert(project);
+ var list = _projectCollection.AsQueryable().FirstOrDefault(p => p.Id.Equals(projectId)).Images;
+ list.Remove(imageId);
+ var update = Builders.Update
+ .Set("Images", list)
+ .CurrentDate("LastModified");
+ var result = _projectCollection.UpdateOne(p => p.Id.Equals(projectId), update);
}
public Project AddProject(Project project)
{
- _projectCollection.Insert(project);
- return project;
+ _projectCollection.InsertOne(project);
+ return project;
}
public List GetAllProject()
{
//ToDo delete this bad method
- return _projectCollection.FindAll().ToList();
+ return _projectCollection.AsQueryable().ToList();
}
public Project GetProjectById(ObjectId id)
@@ -61,47 +74,42 @@ public Project GetProjectById(ObjectId id)
return _projectCollection.AsQueryable().FirstOrDefault(p => p.Id.Equals(id));
}
- public void AddProjectToProject(ObjectId newProject, ObjectId iDRootProject)
+ public void AddProjectToProject(ObjectId newProject, ObjectId idRootProject)
{
- var project = _projectCollection.AsQueryable().FirstOrDefault(im => im.Id.Equals(iDRootProject));
- _projectCollection.Remove(Query.EQ("_id", iDRootProject));
- project.Comments.Add(newProject);
- _projectCollection.Insert(project);
+ var update = Builders.Update
+ .AddToSet("Projects", newProject)
+ .CurrentDate("LastModified");
+ var result = _projectCollection.UpdateOne(p => p.Id.Equals(idRootProject), update);
}
- public void AddImageToProject(ObjectId newImage, ObjectId iDProject)
+ public void AddImageToProject(ObjectId newImage, ObjectId idProject)
{
- var project = _projectCollection.AsQueryable().FirstOrDefault(im => im.Id.Equals(iDProject));
- _projectCollection.Remove(Query.EQ("_id", iDProject));
- project.Comments.Add(newImage);
- _projectCollection.Insert(project);
+ var update = Builders.Update
+ .AddToSet("Images", newImage)
+ .CurrentDate("LastModified");
+ var result = _projectCollection.UpdateOne(p => p.Id.Equals(idProject), update);
}
- public void AddCommentToProject(ObjectId newComment, ObjectId iDProject)
+ public void AddCommentToProject(ObjectId newComment, ObjectId idProject)
{
- var project = _projectCollection.AsQueryable().FirstOrDefault(im => im.Id.Equals(iDProject));
- _projectCollection.Remove(Query.EQ("_id", iDProject));
- project.Comments.Add(newComment);
- _projectCollection.Insert(project);
+ var update = Builders.Update
+ .AddToSet("Comments", newComment)
+ .CurrentDate("LastModified");
+ var result = _projectCollection.UpdateOne(p => p.Id.Equals(idProject), update);
}
public List GetProjectsByIds(List ids)
{
- var list = _projectCollection.FindAll().ToList();
- HashSet id = new HashSet();
- foreach (ObjectId i in ids)
- {
- id.Add(i);
- }
- var projects = new List();
- foreach (Project p in list)
- {
- if (id.Contains(p.Id))
- {
- projects.Add(p);
- }
- }
- return projects;
+ return _projectCollection.AsQueryable().Where(p => ids.Contains(p.Id)).ToList();
+ }
+
+ public void UpdateProject(ObjectId id, string name, uint version)
+ {
+ var update = Builders.Update
+ .Set("Name", name)
+ .Set("Version", version)
+ .CurrentDate("LastModified");
+ var result = _projectCollection.UpdateOne(p => p.Id.Equals(id), update);
}
}
}
diff --git a/Ease-L.sln b/Ease-L.sln
index 567f240..88e077c 100644
--- a/Ease-L.sln
+++ b/Ease-L.sln
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApp", "WebApp\WebApp.csp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DB", "DB\DB.csproj", "{4841AB80-72FD-4F53-BB68-28B111786669}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControllersLogic", "ControllersLogic\ControllersLogic.csproj", "{89FC8B31-B22A-40DD-981E-89A86D96BE34}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{4841AB80-72FD-4F53-BB68-28B111786669}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4841AB80-72FD-4F53-BB68-28B111786669}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4841AB80-72FD-4F53-BB68-28B111786669}.Release|Any CPU.Build.0 = Release|Any CPU
+ {89FC8B31-B22A-40DD-981E-89A86D96BE34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {89FC8B31-B22A-40DD-981E-89A86D96BE34}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {89FC8B31-B22A-40DD-981E-89A86D96BE34}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {89FC8B31-B22A-40DD-981E-89A86D96BE34}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/WebApp/App_Data/XmlDocument.xml b/WebApp/App_Data/XmlDocument.xml
new file mode 100644
index 0000000..d0093b1
--- /dev/null
+++ b/WebApp/App_Data/XmlDocument.xml
@@ -0,0 +1,687 @@
+
+
+
+ WebApp
+
+
+
+
+ Generates an URI-friendly ID for the . E.g. "Get-Values-id_name" instead of "GetValues/{id}?name={name}"
+
+ The .
+ The ID as a string.
+
+
+
+ Use this class to customize the Help Page.
+ For example you can set a custom to supply the documentation
+ or you can provide the samples for the requests/responses.
+
+
+
+
+ The controller that will handle requests for the help page.
+
+
+
+
+ Sets the documentation provider for help page.
+
+ The .
+ The documentation provider.
+
+
+
+ Sets the objects that will be used by the formatters to produce sample requests/responses.
+
+ The .
+ The sample objects.
+
+
+
+ Sets the sample request directly for the specified media type and action.
+
+ The .
+ The sample request.
+ The media type.
+ Name of the controller.
+ Name of the action.
+
+
+
+ Sets the sample request directly for the specified media type and action with parameters.
+
+ The .
+ The sample request.
+ The media type.
+ Name of the controller.
+ Name of the action.
+ The parameter names.
+
+
+
+ Sets the sample request directly for the specified media type of the action.
+
+ The .
+ The sample response.
+ The media type.
+ Name of the controller.
+ Name of the action.
+
+
+
+ Sets the sample response directly for the specified media type of the action with specific parameters.
+
+ The .
+ The sample response.
+ The media type.
+ Name of the controller.
+ Name of the action.
+ The parameter names.
+
+
+
+ Sets the sample directly for all actions with the specified media type.
+
+ The .
+ The sample.
+ The media type.
+
+
+
+ Sets the sample directly for all actions with the specified type and media type.
+
+ The .
+ The sample.
+ The media type.
+ The parameter type or return type of an action.
+
+
+
+ Specifies the actual type of passed to the in an action.
+ The help page will use this information to produce more accurate request samples.
+
+ The .
+ The type.
+ Name of the controller.
+ Name of the action.
+
+
+
+ Specifies the actual type of passed to the in an action.
+ The help page will use this information to produce more accurate request samples.
+
+ The .
+ The type.
+ Name of the controller.
+ Name of the action.
+ The parameter names.
+
+
+
+ Specifies the actual type of returned as part of the in an action.
+ The help page will use this information to produce more accurate response samples.
+
+ The .
+ The type.
+ Name of the controller.
+ Name of the action.
+
+
+
+ Specifies the actual type of returned as part of the in an action.
+ The help page will use this information to produce more accurate response samples.
+
+ The .
+ The type.
+ Name of the controller.
+ Name of the action.
+ The parameter names.
+
+
+
+ Gets the help page sample generator.
+
+ The .
+ The help page sample generator.
+
+
+
+ Sets the help page sample generator.
+
+ The .
+ The help page sample generator.
+
+
+
+ Gets the model description generator.
+
+ The configuration.
+ The
+
+
+
+ Gets the model that represents an API displayed on the help page. The model is initialized on the first call and cached for subsequent calls.
+
+ The .
+ The ID.
+
+ An
+
+
+
+
+ Describes a type model.
+
+
+
+
+ Generates model descriptions for given types.
+
+
+
+
+ Use this attribute to change the name of the generated for a type.
+
+
+
+
+ The model that represents an API displayed on the help page.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Gets or sets the that describes the API.
+
+
+
+
+ Gets or sets the collection that describes the URI parameters for the API.
+
+
+
+
+ Gets or sets the documentation for the request.
+
+
+
+
+ Gets or sets the that describes the request body.
+
+
+
+
+ Gets the request body parameter descriptions.
+
+
+
+
+ Gets or sets the that describes the resource.
+
+
+
+
+ Gets the resource property descriptions.
+
+
+
+
+ Gets the sample requests associated with the API.
+
+
+
+
+ Gets the sample responses associated with the API.
+
+
+
+
+ Gets the error messages associated with this model.
+
+
+
+
+ This class will generate the samples for the help page.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Gets CLR types that are used as the content of or .
+
+
+
+
+ Gets the objects that are used directly as samples for certain actions.
+
+
+
+
+ Gets the objects that are serialized as samples by the supported formatters.
+
+
+
+
+ Gets factories for the objects that the supported formatters will serialize as samples. Processed in order,
+ stopping when the factory successfully returns a non- object.
+
+
+ Collection includes just initially. Use
+ SampleObjectFactories.Insert(0, func) to provide an override and
+ SampleObjectFactories.Add(func) to provide a fallback.
+
+
+
+ Gets the request body samples for a given .
+
+ The .
+ The samples keyed by media type.
+
+
+
+ Gets the response body samples for a given .
+
+ The .
+ The samples keyed by media type.
+
+
+
+ Gets the request or response body samples.
+
+ The .
+ The value indicating whether the sample is for a request or for a response.
+ The samples keyed by media type.
+
+
+
+ Search for samples that are provided directly through .
+
+ Name of the controller.
+ Name of the action.
+ The parameter names.
+ The CLR type.
+ The formatter.
+ The media type.
+ The value indicating whether the sample is for a request or for a response.
+ The sample that matches the parameters.
+
+
+
+ Gets the sample object that will be serialized by the formatters.
+ First, it will look at the . If no sample object is found, it will try to create
+ one using (which wraps an ) and other
+ factories in .
+
+ The type.
+ The sample object.
+
+
+
+ Resolves the actual type of passed to the in an action.
+
+ The .
+ The type.
+
+
+
+ Resolves the type of the action parameter or return value when or is used.
+
+ The .
+ Name of the controller.
+ Name of the action.
+ The parameter names.
+ The value indicating whether the sample is for a request or a response.
+ The formatters.
+
+
+
+ Writes the sample object using formatter.
+
+ The formatter.
+ The value.
+ The type.
+ Type of the media.
+
+
+
+
+ This is used to identify the place where the sample should be applied.
+
+
+
+
+ Creates a new based on media type.
+
+ The media type.
+
+
+
+ Creates a new based on media type and CLR type.
+
+ The media type.
+ The CLR type.
+
+
+
+ Creates a new based on , controller name, action name and parameter names.
+
+ The .
+ Name of the controller.
+ Name of the action.
+ The parameter names.
+
+
+
+ Creates a new based on media type, , controller name, action name and parameter names.
+
+ The media type.
+ The .
+ Name of the controller.
+ Name of the action.
+ The parameter names.
+
+
+
+ Gets the name of the controller.
+
+
+ The name of the controller.
+
+
+
+
+ Gets the name of the action.
+
+
+ The name of the action.
+
+
+
+
+ Gets the media type.
+
+
+ The media type.
+
+
+
+
+ Gets the parameter names.
+
+
+
+
+ Gets the .
+
+
+
+
+ This represents an image sample on the help page. There's a display template named ImageSample associated with this class.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The URL of an image.
+
+
+
+ This represents an invalid sample on the help page. There's a display template named InvalidSample associated with this class.
+
+
+
+
+ This class will create an object of a given type and populate it with sample data.
+
+
+
+
+ Generates an object for a given type. The type needs to be public, have a public default constructor and settable public properties/fields. Currently it supports the following types:
+ Simple types: , , , , , etc.
+ Complex types: POCO types.
+ Nullables: .
+ Arrays: arrays of simple types or complex types.
+ Key value pairs:
+ Tuples: , , etc
+ Dictionaries: or anything deriving from .
+ Collections: , , , , , or anything deriving from or .
+ Queryables: , .
+
+ The type.
+ An object of the given type.
+
+
+
+ Indicates whether the sample is used for request or response
+
+
+
+
+ This represents a preformatted text sample on the help page. There's a display template named TextSample associated with this class.
+
+
+
+
+ A custom that reads the API documentation from an XML documentation file.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The physical path to XML document.
+
+
+
+ Make new comment.
+
+ Text
+ Name
+ Return id new comment
+
+
+
+ Get all comments
+
+ all comments
+
+
+
+ Get comment by id
+
+ Id
+
+
+
+
+ Update comment by id
+
+ Id
+ New name
+ New text
+ New Comment
+
+
+
+ Add comment to image
+
+ Image id
+ Comment text
+ Comment name
+ Id new comment
+
+
+
+ Make new image
+
+ Image url
+ Name
+ Id new image
+
+
+
+ Delete comment from image
+
+ Imgae id
+ Comment id
+ Result
+
+
+
+ Download image for getting url
+
+ Uploded image, HttpPostedFileBase
+ Image url
+
+
+
+ Get all image
+
+ List image
+
+
+
+ Get image by id
+
+ Id
+ Image
+
+
+
+ Get image by id and version
+
+ Id
+ Version
+ Image
+
+
+
+ Get comments from image
+
+ Image id
+ List comments
+
+
+
+ Update image
+
+ Id
+ New name
+ New url
+ New Image
+
+
+
+ Content-Type application/json
+
+
+
+
+ Add new comment to project
+
+ Text
+ Name
+ Project id
+ Id new project
+
+
+
+ Add new image to ptoject
+
+ Image url
+ Name
+ Project id
+ Id new image
+
+
+
+ Add new project
+
+ Name
+ Id new project
+
+
+
+ Add new project into ptoject
+
+ Id root project
+ Name new project
+ Id new project
+
+
+
+ Delete project by id
+
+ Deleted project id
+ Result
+
+
+
+ GetAllProject
+
+ List project
+
+
+
+ Delete comment from project
+
+ Project id
+ Comment id
+ Result
+
+
+
+ Delete image from project
+
+ Project id
+ Image id
+ Result
+
+
+
+ Get project by id
+
+ Id
+ Project
+
+
+
+ Get comment from project
+
+ Id project
+ List id comments
+
+
+
+ Get image from project
+
+ Id project
+ List id images
+
+
+
+ Get project from project
+
+ Id root project
+ List id projects
+
+
+
+ Update project by id
+
+ Id
+ New name
+ New project
+
+
+
diff --git a/WebApp/App_Start/ApplicationIdentityContext.cs b/WebApp/App_Start/ApplicationIdentityContext.cs
deleted file mode 100644
index 103b53e..0000000
--- a/WebApp/App_Start/ApplicationIdentityContext.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using DB;
-using Microsoft.AspNet.Identity.EntityFramework;
-using MongoDB.Driver;
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using System.Web;
-using WebApp.Models;
-
-namespace WebApp.App_Start
-{
- public class ApplicationIdentityContext : IDisposable
- {
- /*public static ApplicationIdentityContext Create()
- {
- // todo add settings where appropriate to switch server & database in your own application
- var database = MongoClientFactory.GetMongoDatabase();
- var users = database.GetCollection("users");
- var roles = database.GetCollection("roles");
- return new ApplicationIdentityContext(users, roles);
- }*/
-
- private ApplicationIdentityContext(IMongoCollection users, IMongoCollection roles)
- {
- Users = users;
- Roles = roles;
- }
-
- public IMongoCollection Roles { get; set; }
-
- public IMongoCollection Users { get; set; }
-
- public Task> AllRolesAsync()
- {
- return Roles.Find(r => true).ToListAsync();
- }
-
- public void Dispose()
- {
- }
- }
-}
\ No newline at end of file
diff --git a/WebApp/App_Start/FilterConfig.cs b/WebApp/App_Start/FilterConfig.cs
index bc9608d..8df75cd 100644
--- a/WebApp/App_Start/FilterConfig.cs
+++ b/WebApp/App_Start/FilterConfig.cs
@@ -8,6 +8,7 @@ public class FilterConfig
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
+
}
}
}
diff --git a/WebApp/App_Start/IdentityConfig.cs b/WebApp/App_Start/IdentityConfig.cs
index 0da69bd..c50ece0 100644
--- a/WebApp/App_Start/IdentityConfig.cs
+++ b/WebApp/App_Start/IdentityConfig.cs
@@ -11,7 +11,6 @@
using Microsoft.Owin;
using Microsoft.Owin.Security;
using WebApp.Models;
-using WebApp.App_Start;
namespace WebApp
{
@@ -87,54 +86,6 @@ public static ApplicationUserManager Create(IdentityFactoryOptions AddUserToRolesAsync(string userId, IList roles)
- {
- var userRoleStore = (IUserRoleStore)Store;
-
- var user = await FindByIdAsync(userId).ConfigureAwait(false);
- if (user == null)
- {
- throw new InvalidOperationException("Invalid user Id");
- }
-
- var userRoles = await userRoleStore.GetRolesAsync(user).ConfigureAwait(false);
- // Add user to each role using UserRoleStore
- foreach (var role in roles.Where(role => !userRoles.Contains(role)))
- {
- await userRoleStore.AddToRoleAsync(user, role).ConfigureAwait(false);
- }
-
- // Call update once when all roles are added
- return await UpdateAsync(user).ConfigureAwait(false);
- }
-
- ///
- /// Remove user from multiple roles
- ///
- /// user id
- /// list of role names
- ///
- public virtual async Task RemoveUserFromRolesAsync(string userId, IList roles)
- {
- var userRoleStore = (IUserRoleStore)Store;
-
- var user = await FindByIdAsync(userId).ConfigureAwait(false);
- if (user == null)
- {
- throw new InvalidOperationException("Invalid user Id");
- }
-
- var userRoles = await userRoleStore.GetRolesAsync(user).ConfigureAwait(false);
- // Remove user to each role using UserRoleStore
- foreach (var role in roles.Where(userRoles.Contains))
- {
- await userRoleStore.RemoveFromRoleAsync(user, role).ConfigureAwait(false);
- }
-
- // Call update once when all roles are removed
- return await UpdateAsync(user).ConfigureAwait(false);
- }
}
// Configure the application sign-in manager which is used in this application.
diff --git a/WebApp/App_Start/RouteConfig.cs b/WebApp/App_Start/RouteConfig.cs
index 3c9ac3b..431d5a3 100644
--- a/WebApp/App_Start/RouteConfig.cs
+++ b/WebApp/App_Start/RouteConfig.cs
@@ -4,6 +4,7 @@
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
+using WebApp.Areas.HelpPage;
namespace WebApp
{
@@ -14,7 +15,6 @@ public static void RegisterRoutes(RouteCollection routes)
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
-
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
diff --git a/WebApp/App_Start/WebApiConfig.cs b/WebApp/App_Start/WebApiConfig.cs
new file mode 100644
index 0000000..90d4ff2
--- /dev/null
+++ b/WebApp/App_Start/WebApiConfig.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http.Headers;
+using System.Web;
+using System.Web.Http;
+using WebApp.Areas.HelpPage;
+
+namespace WebApp.App_Start
+{
+ public static class WebApiConfig
+ {
+ public static void Register(HttpConfiguration config)
+ {
+ // Web API configuration and services
+
+ // Web API routes
+ config.MapHttpAttributeRoutes();
+ //config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
+ var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
+ config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
+ config.Routes.MapHttpRoute(
+ name: "DefaultApi",
+ routeTemplate: "api/{controller}/{id}",
+ defaults: new { id = RouteParameter.Optional }
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/ApiDescriptionExtensions.cs b/WebApp/Areas/HelpPage/ApiDescriptionExtensions.cs
new file mode 100644
index 0000000..a6461f0
--- /dev/null
+++ b/WebApp/Areas/HelpPage/ApiDescriptionExtensions.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Text;
+using System.Web;
+using System.Web.Http.Description;
+
+namespace WebApp.Areas.HelpPage
+{
+ public static class ApiDescriptionExtensions
+ {
+ ///
+ /// Generates an URI-friendly ID for the . E.g. "Get-Values-id_name" instead of "GetValues/{id}?name={name}"
+ ///
+ /// The .
+ /// The ID as a string.
+ public static string GetFriendlyId(this ApiDescription description)
+ {
+ string path = description.RelativePath;
+ string[] urlParts = path.Split('?');
+ string localPath = urlParts[0];
+ string queryKeyString = null;
+ if (urlParts.Length > 1)
+ {
+ string query = urlParts[1];
+ string[] queryKeys = HttpUtility.ParseQueryString(query).AllKeys;
+ queryKeyString = String.Join("_", queryKeys);
+ }
+
+ StringBuilder friendlyPath = new StringBuilder();
+ friendlyPath.AppendFormat("{0}-{1}",
+ description.HttpMethod.Method,
+ localPath.Replace("/", "-").Replace("{", String.Empty).Replace("}", String.Empty));
+ if (queryKeyString != null)
+ {
+ friendlyPath.AppendFormat("_{0}", queryKeyString.Replace('.', '-'));
+ }
+ return friendlyPath.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/App_Start/HelpPageConfig.cs b/WebApp/Areas/HelpPage/App_Start/HelpPageConfig.cs
new file mode 100644
index 0000000..841188a
--- /dev/null
+++ b/WebApp/Areas/HelpPage/App_Start/HelpPageConfig.cs
@@ -0,0 +1,114 @@
+// Uncomment the following to provide samples for PageResult. Must also add the Microsoft.AspNet.WebApi.OData
+// package to your project.
+////#define Handle_PageResultOfT
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Net.Http.Headers;
+using System.Reflection;
+using System.Web;
+using System.Web.Http;
+#if Handle_PageResultOfT
+using System.Web.Http.OData;
+#endif
+
+namespace WebApp.Areas.HelpPage
+{
+ ///
+ /// Use this class to customize the Help Page.
+ /// For example you can set a custom to supply the documentation
+ /// or you can provide the samples for the requests/responses.
+ ///
+ public static class HelpPageConfig
+ {
+ [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters",
+ MessageId = "WebApp.Areas.HelpPage.TextSample.#ctor(System.String)",
+ Justification = "End users may choose to merge this string with existing localized resources.")]
+ [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly",
+ MessageId = "bsonspec",
+ Justification = "Part of a URI.")]
+ public static void Register(HttpConfiguration config)
+ {
+ //// Uncomment the following to use the documentation from XML documentation file.
+ //config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
+
+ //// Uncomment the following to use "sample string" as the sample for all actions that have string as the body parameter or return type.
+ //// Also, the string arrays will be used for IEnumerable. The sample objects will be serialized into different media type
+ //// formats by the available formatters.
+ //config.SetSampleObjects(new Dictionary
+ //{
+ // {typeof(string), "sample string"},
+ // {typeof(IEnumerable), new string[]{"sample 1", "sample 2"}}
+ //});
+
+ // Extend the following to provide factories for types not handled automatically (those lacking parameterless
+ // constructors) or for which you prefer to use non-default property values. Line below provides a fallback
+ // since automatic handling will fail and GeneratePageResult handles only a single type.
+#if Handle_PageResultOfT
+ config.GetHelpPageSampleGenerator().SampleObjectFactories.Add(GeneratePageResult);
+#endif
+
+ // Extend the following to use a preset object directly as the sample for all actions that support a media
+ // type, regardless of the body parameter or return type. The lines below avoid display of binary content.
+ // The BsonMediaTypeFormatter (if available) is not used to serialize the TextSample object.
+ config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
+ config.SetSampleForMediaType(
+ new TextSample("Binary JSON content. See http://bsonspec.org for details."),
+ new MediaTypeHeaderValue("application/bson"));
+
+ //// Uncomment the following to use "[0]=foo&[1]=bar" directly as the sample for all actions that support form URL encoded format
+ //// and have IEnumerable as the body parameter or return type.
+ //config.SetSampleForType("[0]=foo&[1]=bar", new MediaTypeHeaderValue("application/x-www-form-urlencoded"), typeof(IEnumerable));
+
+ //// Uncomment the following to use "1234" directly as the request sample for media type "text/plain" on the controller named "Values"
+ //// and action named "Put".
+ //config.SetSampleRequest("1234", new MediaTypeHeaderValue("text/plain"), "Values", "Put");
+
+ //// Uncomment the following to use the image on "../images/aspNetHome.png" directly as the response sample for media type "image/png"
+ //// on the controller named "Values" and action named "Get" with parameter "id".
+ //config.SetSampleResponse(new ImageSample("../images/aspNetHome.png"), new MediaTypeHeaderValue("image/png"), "Values", "Get", "id");
+
+ //// Uncomment the following to correct the sample request when the action expects an HttpRequestMessage with ObjectContent.
+ //// The sample will be generated as if the controller named "Values" and action named "Get" were having string as the body parameter.
+ //config.SetActualRequestType(typeof(string), "Values", "Get");
+
+ //// Uncomment the following to correct the sample response when the action returns an HttpResponseMessage with ObjectContent.
+ //// The sample will be generated as if the controller named "Values" and action named "Post" were returning a string.
+ //config.SetActualResponseType(typeof(string), "Values", "Post");
+ }
+
+#if Handle_PageResultOfT
+ private static object GeneratePageResult(HelpPageSampleGenerator sampleGenerator, Type type)
+ {
+ if (type.IsGenericType)
+ {
+ Type openGenericType = type.GetGenericTypeDefinition();
+ if (openGenericType == typeof(PageResult<>))
+ {
+ // Get the T in PageResult
+ Type[] typeParameters = type.GetGenericArguments();
+ Debug.Assert(typeParameters.Length == 1);
+
+ // Create an enumeration to pass as the first parameter to the PageResult constuctor
+ Type itemsType = typeof(List<>).MakeGenericType(typeParameters);
+ object items = sampleGenerator.GetSampleObject(itemsType);
+
+ // Fill in the other information needed to invoke the PageResult constuctor
+ Type[] parameterTypes = new Type[] { itemsType, typeof(Uri), typeof(long?), };
+ object[] parameters = new object[] { items, null, (long)ObjectGenerator.DefaultCollectionSize, };
+
+ // Call PageResult(IEnumerable items, Uri nextPageLink, long? count) constructor
+ ConstructorInfo constructor = type.GetConstructor(parameterTypes);
+ return constructor.Invoke(parameters);
+ }
+ }
+
+ return null;
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/Controllers/HelpController.cs b/WebApp/Areas/HelpPage/Controllers/HelpController.cs
new file mode 100644
index 0000000..7795596
--- /dev/null
+++ b/WebApp/Areas/HelpPage/Controllers/HelpController.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Web.Http;
+using System.Web.Mvc;
+using WebApp.Areas.HelpPage.ModelDescriptions;
+using WebApp.Areas.HelpPage.Models;
+
+namespace WebApp.Areas.HelpPage.Controllers
+{
+ ///
+ /// The controller that will handle requests for the help page.
+ ///
+ public class HelpController : Controller
+ {
+ private const string ErrorViewName = "Error";
+
+ public HelpController()
+ : this(GlobalConfiguration.Configuration)
+ {
+ }
+
+ public HelpController(HttpConfiguration config)
+ {
+ Configuration = config;
+ }
+
+ public HttpConfiguration Configuration { get; private set; }
+
+ public ActionResult Index()
+ {
+ ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider();
+ return View(Configuration.Services.GetApiExplorer().ApiDescriptions);
+ }
+
+ public ActionResult Api(string apiId)
+ {
+ if (!String.IsNullOrEmpty(apiId))
+ {
+ HelpPageApiModel apiModel = Configuration.GetHelpPageApiModel(apiId);
+ if (apiModel != null)
+ {
+ return View(apiModel);
+ }
+ }
+
+ return View(ErrorViewName);
+ }
+
+ public ActionResult ResourceModel(string modelName)
+ {
+ if (!String.IsNullOrEmpty(modelName))
+ {
+ ModelDescriptionGenerator modelDescriptionGenerator = Configuration.GetModelDescriptionGenerator();
+ ModelDescription modelDescription;
+ if (modelDescriptionGenerator.GeneratedModels.TryGetValue(modelName, out modelDescription))
+ {
+ return View(modelDescription);
+ }
+ }
+
+ return View(ErrorViewName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/HelpPage.css b/WebApp/Areas/HelpPage/HelpPage.css
new file mode 100644
index 0000000..aff2230
--- /dev/null
+++ b/WebApp/Areas/HelpPage/HelpPage.css
@@ -0,0 +1,134 @@
+.help-page h1,
+.help-page .h1,
+.help-page h2,
+.help-page .h2,
+.help-page h3,
+.help-page .h3,
+#body.help-page,
+.help-page-table th,
+.help-page-table pre,
+.help-page-table p {
+ font-family: "Segoe UI Light", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
+}
+
+.help-page pre.wrapped {
+ white-space: -moz-pre-wrap;
+ white-space: -pre-wrap;
+ white-space: -o-pre-wrap;
+ white-space: pre-wrap;
+}
+
+.help-page .warning-message-container {
+ margin-top: 20px;
+ padding: 0 10px;
+ color: #525252;
+ background: #EFDCA9;
+ border: 1px solid #CCCCCC;
+}
+
+.help-page-table {
+ width: 100%;
+ border-collapse: collapse;
+ text-align: left;
+ margin: 0px 0px 20px 0px;
+ border-top: 1px solid #D4D4D4;
+}
+
+.help-page-table th {
+ text-align: left;
+ font-weight: bold;
+ border-bottom: 1px solid #D4D4D4;
+ padding: 5px 6px 5px 6px;
+}
+
+.help-page-table td {
+ border-bottom: 1px solid #D4D4D4;
+ padding: 10px 8px 10px 8px;
+ vertical-align: top;
+}
+
+.help-page-table pre,
+.help-page-table p {
+ margin: 0px;
+ padding: 0px;
+ font-family: inherit;
+ font-size: 100%;
+}
+
+.help-page-table tbody tr:hover td {
+ background-color: #F3F3F3;
+}
+
+.help-page a:hover {
+ background-color: transparent;
+}
+
+.help-page .sample-header {
+ border: 2px solid #D4D4D4;
+ background: #00497E;
+ color: #FFFFFF;
+ padding: 8px 15px;
+ border-bottom: none;
+ display: inline-block;
+ margin: 10px 0px 0px 0px;
+}
+
+.help-page .sample-content {
+ display: block;
+ border-width: 0;
+ padding: 15px 20px;
+ background: #FFFFFF;
+ border: 2px solid #D4D4D4;
+ margin: 0px 0px 10px 0px;
+}
+
+.help-page .api-name {
+ width: 40%;
+}
+
+.help-page .api-documentation {
+ width: 60%;
+}
+
+.help-page .parameter-name {
+ width: 20%;
+}
+
+.help-page .parameter-documentation {
+ width: 40%;
+}
+
+.help-page .parameter-type {
+ width: 20%;
+}
+
+.help-page .parameter-annotations {
+ width: 20%;
+}
+
+.help-page h1,
+.help-page .h1 {
+ font-size: 36px;
+ line-height: normal;
+}
+
+.help-page h2,
+.help-page .h2 {
+ font-size: 24px;
+}
+
+.help-page h3,
+.help-page .h3 {
+ font-size: 20px;
+}
+
+#body.help-page {
+ font-size: 14px;
+ line-height: 143%;
+ color: #333;
+}
+
+.help-page a {
+ color: #0000EE;
+ text-decoration: none;
+}
diff --git a/WebApp/Areas/HelpPage/HelpPageAreaRegistration.cs b/WebApp/Areas/HelpPage/HelpPageAreaRegistration.cs
new file mode 100644
index 0000000..d0405c7
--- /dev/null
+++ b/WebApp/Areas/HelpPage/HelpPageAreaRegistration.cs
@@ -0,0 +1,26 @@
+using System.Web.Http;
+using System.Web.Mvc;
+
+namespace WebApp.Areas.HelpPage
+{
+ public class HelpPageAreaRegistration : AreaRegistration
+ {
+ public override string AreaName
+ {
+ get
+ {
+ return "HelpPage";
+ }
+ }
+
+ public override void RegisterArea(AreaRegistrationContext context)
+ {
+ context.MapRoute(
+ "HelpPage_Default",
+ "Help/{action}/{apiId}",
+ new { controller = "Help", action = "Index", apiId = UrlParameter.Optional });
+
+ HelpPageConfig.Register(GlobalConfiguration.Configuration);
+ }
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/HelpPageConfigurationExtensions.cs b/WebApp/Areas/HelpPage/HelpPageConfigurationExtensions.cs
new file mode 100644
index 0000000..ffd881b
--- /dev/null
+++ b/WebApp/Areas/HelpPage/HelpPageConfigurationExtensions.cs
@@ -0,0 +1,467 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Web.Http;
+using System.Web.Http.Controllers;
+using System.Web.Http.Description;
+using WebApp.Areas.HelpPage.ModelDescriptions;
+using WebApp.Areas.HelpPage.Models;
+
+namespace WebApp.Areas.HelpPage
+{
+ public static class HelpPageConfigurationExtensions
+ {
+ private const string ApiModelPrefix = "MS_HelpPageApiModel_";
+
+ ///
+ /// Sets the documentation provider for help page.
+ ///
+ /// The .
+ /// The documentation provider.
+ public static void SetDocumentationProvider(this HttpConfiguration config, IDocumentationProvider documentationProvider)
+ {
+ config.Services.Replace(typeof(IDocumentationProvider), documentationProvider);
+ }
+
+ ///
+ /// Sets the objects that will be used by the formatters to produce sample requests/responses.
+ ///
+ /// The .
+ /// The sample objects.
+ public static void SetSampleObjects(this HttpConfiguration config, IDictionary sampleObjects)
+ {
+ config.GetHelpPageSampleGenerator().SampleObjects = sampleObjects;
+ }
+
+ ///
+ /// Sets the sample request directly for the specified media type and action.
+ ///
+ /// The .
+ /// The sample request.
+ /// The media type.
+ /// Name of the controller.
+ /// Name of the action.
+ public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName)
+ {
+ config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, new[] { "*" }), sample);
+ }
+
+ ///
+ /// Sets the sample request directly for the specified media type and action with parameters.
+ ///
+ /// The .
+ /// The sample request.
+ /// The media type.
+ /// Name of the controller.
+ /// Name of the action.
+ /// The parameter names.
+ public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames)
+ {
+ config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, parameterNames), sample);
+ }
+
+ ///
+ /// Sets the sample request directly for the specified media type of the action.
+ ///
+ /// The .
+ /// The sample response.
+ /// The media type.
+ /// Name of the controller.
+ /// Name of the action.
+ public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName)
+ {
+ config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, new[] { "*" }), sample);
+ }
+
+ ///
+ /// Sets the sample response directly for the specified media type of the action with specific parameters.
+ ///
+ /// The .
+ /// The sample response.
+ /// The media type.
+ /// Name of the controller.
+ /// Name of the action.
+ /// The parameter names.
+ public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames)
+ {
+ config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, parameterNames), sample);
+ }
+
+ ///
+ /// Sets the sample directly for all actions with the specified media type.
+ ///
+ /// The .
+ /// The sample.
+ /// The media type.
+ public static void SetSampleForMediaType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType)
+ {
+ config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType), sample);
+ }
+
+ ///
+ /// Sets the sample directly for all actions with the specified type and media type.
+ ///
+ /// The .
+ /// The sample.
+ /// The media type.
+ /// The parameter type or return type of an action.
+ public static void SetSampleForType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, Type type)
+ {
+ config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, type), sample);
+ }
+
+ ///
+ /// Specifies the actual type of passed to the in an action.
+ /// The help page will use this information to produce more accurate request samples.
+ ///
+ /// The .
+ /// The type.
+ /// Name of the controller.
+ /// Name of the action.
+ public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName)
+ {
+ config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, new[] { "*" }), type);
+ }
+
+ ///
+ /// Specifies the actual type of passed to the in an action.
+ /// The help page will use this information to produce more accurate request samples.
+ ///
+ /// The .
+ /// The type.
+ /// Name of the controller.
+ /// Name of the action.
+ /// The parameter names.
+ public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames)
+ {
+ config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, parameterNames), type);
+ }
+
+ ///
+ /// Specifies the actual type of returned as part of the in an action.
+ /// The help page will use this information to produce more accurate response samples.
+ ///
+ /// The .
+ /// The type.
+ /// Name of the controller.
+ /// Name of the action.
+ public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName)
+ {
+ config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, new[] { "*" }), type);
+ }
+
+ ///
+ /// Specifies the actual type of returned as part of the in an action.
+ /// The help page will use this information to produce more accurate response samples.
+ ///
+ /// The .
+ /// The type.
+ /// Name of the controller.
+ /// Name of the action.
+ /// The parameter names.
+ public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames)
+ {
+ config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, parameterNames), type);
+ }
+
+ ///
+ /// Gets the help page sample generator.
+ ///
+ /// The .
+ /// The help page sample generator.
+ public static HelpPageSampleGenerator GetHelpPageSampleGenerator(this HttpConfiguration config)
+ {
+ return (HelpPageSampleGenerator)config.Properties.GetOrAdd(
+ typeof(HelpPageSampleGenerator),
+ k => new HelpPageSampleGenerator());
+ }
+
+ ///
+ /// Sets the help page sample generator.
+ ///
+ /// The .
+ /// The help page sample generator.
+ public static void SetHelpPageSampleGenerator(this HttpConfiguration config, HelpPageSampleGenerator sampleGenerator)
+ {
+ config.Properties.AddOrUpdate(
+ typeof(HelpPageSampleGenerator),
+ k => sampleGenerator,
+ (k, o) => sampleGenerator);
+ }
+
+ ///
+ /// Gets the model description generator.
+ ///
+ /// The configuration.
+ /// The
+ public static ModelDescriptionGenerator GetModelDescriptionGenerator(this HttpConfiguration config)
+ {
+ return (ModelDescriptionGenerator)config.Properties.GetOrAdd(
+ typeof(ModelDescriptionGenerator),
+ k => InitializeModelDescriptionGenerator(config));
+ }
+
+ ///
+ /// Gets the model that represents an API displayed on the help page. The model is initialized on the first call and cached for subsequent calls.
+ ///
+ /// The .
+ /// The ID.
+ ///
+ /// An
+ ///
+ public static HelpPageApiModel GetHelpPageApiModel(this HttpConfiguration config, string apiDescriptionId)
+ {
+ object model;
+ string modelId = ApiModelPrefix + apiDescriptionId;
+ if (!config.Properties.TryGetValue(modelId, out model))
+ {
+ Collection apiDescriptions = config.Services.GetApiExplorer().ApiDescriptions;
+ ApiDescription apiDescription = apiDescriptions.FirstOrDefault(api => String.Equals(api.GetFriendlyId(), apiDescriptionId, StringComparison.OrdinalIgnoreCase));
+ if (apiDescription != null)
+ {
+ model = GenerateApiModel(apiDescription, config);
+ config.Properties.TryAdd(modelId, model);
+ }
+ }
+
+ return (HelpPageApiModel)model;
+ }
+
+ private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config)
+ {
+ HelpPageApiModel apiModel = new HelpPageApiModel()
+ {
+ ApiDescription = apiDescription,
+ };
+
+ ModelDescriptionGenerator modelGenerator = config.GetModelDescriptionGenerator();
+ HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator();
+ GenerateUriParameters(apiModel, modelGenerator);
+ GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator);
+ GenerateResourceDescription(apiModel, modelGenerator);
+ GenerateSamples(apiModel, sampleGenerator);
+
+ return apiModel;
+ }
+
+ private static void GenerateUriParameters(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator)
+ {
+ ApiDescription apiDescription = apiModel.ApiDescription;
+ foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions)
+ {
+ if (apiParameter.Source == ApiParameterSource.FromUri)
+ {
+ HttpParameterDescriptor parameterDescriptor = apiParameter.ParameterDescriptor;
+ Type parameterType = null;
+ ModelDescription typeDescription = null;
+ ComplexTypeModelDescription complexTypeDescription = null;
+ if (parameterDescriptor != null)
+ {
+ parameterType = parameterDescriptor.ParameterType;
+ typeDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
+ complexTypeDescription = typeDescription as ComplexTypeModelDescription;
+ }
+
+ // Example:
+ // [TypeConverter(typeof(PointConverter))]
+ // public class Point
+ // {
+ // public Point(int x, int y)
+ // {
+ // X = x;
+ // Y = y;
+ // }
+ // public int X { get; set; }
+ // public int Y { get; set; }
+ // }
+ // Class Point is bindable with a TypeConverter, so Point will be added to UriParameters collection.
+ //
+ // public class Point
+ // {
+ // public int X { get; set; }
+ // public int Y { get; set; }
+ // }
+ // Regular complex class Point will have properties X and Y added to UriParameters collection.
+ if (complexTypeDescription != null
+ && !IsBindableWithTypeConverter(parameterType))
+ {
+ foreach (ParameterDescription uriParameter in complexTypeDescription.Properties)
+ {
+ apiModel.UriParameters.Add(uriParameter);
+ }
+ }
+ else if (parameterDescriptor != null)
+ {
+ ParameterDescription uriParameter =
+ AddParameterDescription(apiModel, apiParameter, typeDescription);
+
+ if (!parameterDescriptor.IsOptional)
+ {
+ uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Required" });
+ }
+
+ object defaultValue = parameterDescriptor.DefaultValue;
+ if (defaultValue != null)
+ {
+ uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Default value is " + Convert.ToString(defaultValue, CultureInfo.InvariantCulture) });
+ }
+ }
+ else
+ {
+ Debug.Assert(parameterDescriptor == null);
+
+ // If parameterDescriptor is null, this is an undeclared route parameter which only occurs
+ // when source is FromUri. Ignored in request model and among resource parameters but listed
+ // as a simple string here.
+ ModelDescription modelDescription = modelGenerator.GetOrCreateModelDescription(typeof(string));
+ AddParameterDescription(apiModel, apiParameter, modelDescription);
+ }
+ }
+ }
+ }
+
+ private static bool IsBindableWithTypeConverter(Type parameterType)
+ {
+ if (parameterType == null)
+ {
+ return false;
+ }
+
+ return TypeDescriptor.GetConverter(parameterType).CanConvertFrom(typeof(string));
+ }
+
+ private static ParameterDescription AddParameterDescription(HelpPageApiModel apiModel,
+ ApiParameterDescription apiParameter, ModelDescription typeDescription)
+ {
+ ParameterDescription parameterDescription = new ParameterDescription
+ {
+ Name = apiParameter.Name,
+ Documentation = apiParameter.Documentation,
+ TypeDescription = typeDescription,
+ };
+
+ apiModel.UriParameters.Add(parameterDescription);
+ return parameterDescription;
+ }
+
+ private static void GenerateRequestModelDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator, HelpPageSampleGenerator sampleGenerator)
+ {
+ ApiDescription apiDescription = apiModel.ApiDescription;
+ foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions)
+ {
+ if (apiParameter.Source == ApiParameterSource.FromBody)
+ {
+ Type parameterType = apiParameter.ParameterDescriptor.ParameterType;
+ apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
+ apiModel.RequestDocumentation = apiParameter.Documentation;
+ }
+ else if (apiParameter.ParameterDescriptor != null &&
+ apiParameter.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage))
+ {
+ Type parameterType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription);
+
+ if (parameterType != null)
+ {
+ apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
+ }
+ }
+ }
+ }
+
+ private static void GenerateResourceDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator)
+ {
+ ResponseDescription response = apiModel.ApiDescription.ResponseDescription;
+ Type responseType = response.ResponseType ?? response.DeclaredType;
+ if (responseType != null && responseType != typeof(void))
+ {
+ apiModel.ResourceDescription = modelGenerator.GetOrCreateModelDescription(responseType);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded as ErrorMessages.")]
+ private static void GenerateSamples(HelpPageApiModel apiModel, HelpPageSampleGenerator sampleGenerator)
+ {
+ try
+ {
+ foreach (var item in sampleGenerator.GetSampleRequests(apiModel.ApiDescription))
+ {
+ apiModel.SampleRequests.Add(item.Key, item.Value);
+ LogInvalidSampleAsError(apiModel, item.Value);
+ }
+
+ foreach (var item in sampleGenerator.GetSampleResponses(apiModel.ApiDescription))
+ {
+ apiModel.SampleResponses.Add(item.Key, item.Value);
+ LogInvalidSampleAsError(apiModel, item.Value);
+ }
+ }
+ catch (Exception e)
+ {
+ apiModel.ErrorMessages.Add(String.Format(CultureInfo.CurrentCulture,
+ "An exception has occurred while generating the sample. Exception message: {0}",
+ HelpPageSampleGenerator.UnwrapException(e).Message));
+ }
+ }
+
+ private static bool TryGetResourceParameter(ApiDescription apiDescription, HttpConfiguration config, out ApiParameterDescription parameterDescription, out Type resourceType)
+ {
+ parameterDescription = apiDescription.ParameterDescriptions.FirstOrDefault(
+ p => p.Source == ApiParameterSource.FromBody ||
+ (p.ParameterDescriptor != null && p.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage)));
+
+ if (parameterDescription == null)
+ {
+ resourceType = null;
+ return false;
+ }
+
+ resourceType = parameterDescription.ParameterDescriptor.ParameterType;
+
+ if (resourceType == typeof(HttpRequestMessage))
+ {
+ HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator();
+ resourceType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription);
+ }
+
+ if (resourceType == null)
+ {
+ parameterDescription = null;
+ return false;
+ }
+
+ return true;
+ }
+
+ private static ModelDescriptionGenerator InitializeModelDescriptionGenerator(HttpConfiguration config)
+ {
+ ModelDescriptionGenerator modelGenerator = new ModelDescriptionGenerator(config);
+ Collection apis = config.Services.GetApiExplorer().ApiDescriptions;
+ foreach (ApiDescription api in apis)
+ {
+ ApiParameterDescription parameterDescription;
+ Type parameterType;
+ if (TryGetResourceParameter(api, config, out parameterDescription, out parameterType))
+ {
+ modelGenerator.GetOrCreateModelDescription(parameterType);
+ }
+ }
+ return modelGenerator;
+ }
+
+ private static void LogInvalidSampleAsError(HelpPageApiModel apiModel, object sample)
+ {
+ InvalidSample invalidSample = sample as InvalidSample;
+ if (invalidSample != null)
+ {
+ apiModel.ErrorMessages.Add(invalidSample.ErrorMessage);
+ }
+ }
+ }
+}
diff --git a/WebApp/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs b/WebApp/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs
new file mode 100644
index 0000000..4ed4729
--- /dev/null
+++ b/WebApp/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs
@@ -0,0 +1,7 @@
+namespace WebApp.Areas.HelpPage.ModelDescriptions
+{
+ public class CollectionModelDescription : ModelDescription
+ {
+ public ModelDescription ElementDescription { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs b/WebApp/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs
new file mode 100644
index 0000000..981b55c
--- /dev/null
+++ b/WebApp/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs
@@ -0,0 +1,14 @@
+using System.Collections.ObjectModel;
+
+namespace WebApp.Areas.HelpPage.ModelDescriptions
+{
+ public class ComplexTypeModelDescription : ModelDescription
+ {
+ public ComplexTypeModelDescription()
+ {
+ Properties = new Collection();
+ }
+
+ public Collection Properties { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs b/WebApp/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs
new file mode 100644
index 0000000..ef2cfc3
--- /dev/null
+++ b/WebApp/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs
@@ -0,0 +1,6 @@
+namespace WebApp.Areas.HelpPage.ModelDescriptions
+{
+ public class DictionaryModelDescription : KeyValuePairModelDescription
+ {
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs b/WebApp/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs
new file mode 100644
index 0000000..394e1ae
--- /dev/null
+++ b/WebApp/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+namespace WebApp.Areas.HelpPage.ModelDescriptions
+{
+ public class EnumTypeModelDescription : ModelDescription
+ {
+ public EnumTypeModelDescription()
+ {
+ Values = new Collection();
+ }
+
+ public Collection Values { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs b/WebApp/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs
new file mode 100644
index 0000000..53617c1
--- /dev/null
+++ b/WebApp/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs
@@ -0,0 +1,11 @@
+namespace WebApp.Areas.HelpPage.ModelDescriptions
+{
+ public class EnumValueDescription
+ {
+ public string Documentation { get; set; }
+
+ public string Name { get; set; }
+
+ public string Value { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs b/WebApp/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs
new file mode 100644
index 0000000..4b55e61
--- /dev/null
+++ b/WebApp/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Reflection;
+
+namespace WebApp.Areas.HelpPage.ModelDescriptions
+{
+ public interface IModelDocumentationProvider
+ {
+ string GetDocumentation(MemberInfo member);
+
+ string GetDocumentation(Type type);
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs b/WebApp/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs
new file mode 100644
index 0000000..a85fb71
--- /dev/null
+++ b/WebApp/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs
@@ -0,0 +1,9 @@
+namespace WebApp.Areas.HelpPage.ModelDescriptions
+{
+ public class KeyValuePairModelDescription : ModelDescription
+ {
+ public ModelDescription KeyModelDescription { get; set; }
+
+ public ModelDescription ValueModelDescription { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/ModelDescriptions/ModelDescription.cs b/WebApp/Areas/HelpPage/ModelDescriptions/ModelDescription.cs
new file mode 100644
index 0000000..fc2dafe
--- /dev/null
+++ b/WebApp/Areas/HelpPage/ModelDescriptions/ModelDescription.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace WebApp.Areas.HelpPage.ModelDescriptions
+{
+ ///
+ /// Describes a type model.
+ ///
+ public abstract class ModelDescription
+ {
+ public string Documentation { get; set; }
+
+ public Type ModelType { get; set; }
+
+ public string Name { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WebApp/Areas/HelpPage/ModelDescriptions/ModelDescriptionGenerator.cs b/WebApp/Areas/HelpPage/ModelDescriptions/ModelDescriptionGenerator.cs
new file mode 100644
index 0000000..2dde690
--- /dev/null
+++ b/WebApp/Areas/HelpPage/ModelDescriptions/ModelDescriptionGenerator.cs
@@ -0,0 +1,451 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel.DataAnnotations;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Web.Http;
+using System.Web.Http.Description;
+using System.Xml.Serialization;
+using Newtonsoft.Json;
+
+namespace WebApp.Areas.HelpPage.ModelDescriptions
+{
+ ///
+ /// Generates model descriptions for given types.
+ ///
+ public class ModelDescriptionGenerator
+ {
+ // Modify this to support more data annotation attributes.
+ private readonly IDictionary> AnnotationTextGenerator = new Dictionary>
+ {
+ { typeof(RequiredAttribute), a => "Required" },
+ { typeof(RangeAttribute), a =>
+ {
+ RangeAttribute range = (RangeAttribute)a;
+ return String.Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}", range.Minimum, range.Maximum);
+ }
+ },
+ { typeof(MaxLengthAttribute), a =>
+ {
+ MaxLengthAttribute maxLength = (MaxLengthAttribute)a;
+ return String.Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length);
+ }
+ },
+ { typeof(MinLengthAttribute), a =>
+ {
+ MinLengthAttribute minLength = (MinLengthAttribute)a;
+ return String.Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length);
+ }
+ },
+ { typeof(StringLengthAttribute), a =>
+ {
+ StringLengthAttribute strLength = (StringLengthAttribute)a;
+ return String.Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}", strLength.MinimumLength, strLength.MaximumLength);
+ }
+ },
+ { typeof(DataTypeAttribute), a =>
+ {
+ DataTypeAttribute dataType = (DataTypeAttribute)a;
+ return String.Format(CultureInfo.CurrentCulture, "Data type: {0}", dataType.CustomDataType ?? dataType.DataType.ToString());
+ }
+ },
+ { typeof(RegularExpressionAttribute), a =>
+ {
+ RegularExpressionAttribute regularExpression = (RegularExpressionAttribute)a;
+ return String.Format(CultureInfo.CurrentCulture, "Matching regular expression pattern: {0}", regularExpression.Pattern);
+ }
+ },
+ };
+
+ // Modify this to add more default documentations.
+ private readonly IDictionary DefaultTypeDocumentation = new Dictionary
+ {
+ { typeof(Int16), "integer" },
+ { typeof(Int32), "integer" },
+ { typeof(Int64), "integer" },
+ { typeof(UInt16), "unsigned integer" },
+ { typeof(UInt32), "unsigned integer" },
+ { typeof(UInt64), "unsigned integer" },
+ { typeof(Byte), "byte" },
+ { typeof(Char), "character" },
+ { typeof(SByte), "signed byte" },
+ { typeof(Uri), "URI" },
+ { typeof(Single), "decimal number" },
+ { typeof(Double), "decimal number" },
+ { typeof(Decimal), "decimal number" },
+ { typeof(String), "string" },
+ { typeof(Guid), "globally unique identifier" },
+ { typeof(TimeSpan), "time interval" },
+ { typeof(DateTime), "date" },
+ { typeof(DateTimeOffset), "date" },
+ { typeof(Boolean), "boolean" },
+ };
+
+ private Lazy _documentationProvider;
+
+ public ModelDescriptionGenerator(HttpConfiguration config)
+ {
+ if (config == null)
+ {
+ throw new ArgumentNullException("config");
+ }
+
+ _documentationProvider = new Lazy(() => config.Services.GetDocumentationProvider() as IModelDocumentationProvider);
+ GeneratedModels = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ }
+
+ public Dictionary GeneratedModels { get; private set; }
+
+ private IModelDocumentationProvider DocumentationProvider
+ {
+ get
+ {
+ return _documentationProvider.Value;
+ }
+ }
+
+ public ModelDescription GetOrCreateModelDescription(Type modelType)
+ {
+ if (modelType == null)
+ {
+ throw new ArgumentNullException("modelType");
+ }
+
+ Type underlyingType = Nullable.GetUnderlyingType(modelType);
+ if (underlyingType != null)
+ {
+ modelType = underlyingType;
+ }
+
+ ModelDescription modelDescription;
+ string modelName = ModelNameHelper.GetModelName(modelType);
+ if (GeneratedModels.TryGetValue(modelName, out modelDescription))
+ {
+ if (modelType != modelDescription.ModelType)
+ {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ "A model description could not be created. Duplicate model name '{0}' was found for types '{1}' and '{2}'. " +
+ "Use the [ModelName] attribute to change the model name for at least one of the types so that it has a unique name.",
+ modelName,
+ modelDescription.ModelType.FullName,
+ modelType.FullName));
+ }
+
+ return modelDescription;
+ }
+
+ if (DefaultTypeDocumentation.ContainsKey(modelType))
+ {
+ return GenerateSimpleTypeModelDescription(modelType);
+ }
+
+ if (modelType.IsEnum)
+ {
+ return GenerateEnumTypeModelDescription(modelType);
+ }
+
+ if (modelType.IsGenericType)
+ {
+ Type[] genericArguments = modelType.GetGenericArguments();
+
+ if (genericArguments.Length == 1)
+ {
+ Type enumerableType = typeof(IEnumerable<>).MakeGenericType(genericArguments);
+ if (enumerableType.IsAssignableFrom(modelType))
+ {
+ return GenerateCollectionModelDescription(modelType, genericArguments[0]);
+ }
+ }
+ if (genericArguments.Length == 2)
+ {
+ Type dictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments);
+ if (dictionaryType.IsAssignableFrom(modelType))
+ {
+ return GenerateDictionaryModelDescription(modelType, genericArguments[0], genericArguments[1]);
+ }
+
+ Type keyValuePairType = typeof(KeyValuePair<,>).MakeGenericType(genericArguments);
+ if (keyValuePairType.IsAssignableFrom(modelType))
+ {
+ return GenerateKeyValuePairModelDescription(modelType, genericArguments[0], genericArguments[1]);
+ }
+ }
+ }
+
+ if (modelType.IsArray)
+ {
+ Type elementType = modelType.GetElementType();
+ return GenerateCollectionModelDescription(modelType, elementType);
+ }
+
+ if (modelType == typeof(NameValueCollection))
+ {
+ return GenerateDictionaryModelDescription(modelType, typeof(string), typeof(string));
+ }
+
+ if (typeof(IDictionary).IsAssignableFrom(modelType))
+ {
+ return GenerateDictionaryModelDescription(modelType, typeof(object), typeof(object));
+ }
+
+ if (typeof(IEnumerable).IsAssignableFrom(modelType))
+ {
+ return GenerateCollectionModelDescription(modelType, typeof(object));
+ }
+
+ return GenerateComplexTypeModelDescription(modelType);
+ }
+
+ // Change this to provide different name for the member.
+ private static string GetMemberName(MemberInfo member, bool hasDataContractAttribute)
+ {
+ JsonPropertyAttribute jsonProperty = member.GetCustomAttribute();
+ if (jsonProperty != null && !String.IsNullOrEmpty(jsonProperty.PropertyName))
+ {
+ return jsonProperty.PropertyName;
+ }
+
+ if (hasDataContractAttribute)
+ {
+ DataMemberAttribute dataMember = member.GetCustomAttribute();
+ if (dataMember != null && !String.IsNullOrEmpty(dataMember.Name))
+ {
+ return dataMember.Name;
+ }
+ }
+
+ return member.Name;
+ }
+
+ private static bool ShouldDisplayMember(MemberInfo member, bool hasDataContractAttribute)
+ {
+ JsonIgnoreAttribute jsonIgnore = member.GetCustomAttribute();
+ XmlIgnoreAttribute xmlIgnore = member.GetCustomAttribute();
+ IgnoreDataMemberAttribute ignoreDataMember = member.GetCustomAttribute();
+ NonSerializedAttribute nonSerialized = member.GetCustomAttribute();
+ ApiExplorerSettingsAttribute apiExplorerSetting = member.GetCustomAttribute();
+
+ bool hasMemberAttribute = member.DeclaringType.IsEnum ?
+ member.GetCustomAttribute() != null :
+ member.GetCustomAttribute() != null;
+
+ // Display member only if all the followings are true:
+ // no JsonIgnoreAttribute
+ // no XmlIgnoreAttribute
+ // no IgnoreDataMemberAttribute
+ // no NonSerializedAttribute
+ // no ApiExplorerSettingsAttribute with IgnoreApi set to true
+ // no DataContractAttribute without DataMemberAttribute or EnumMemberAttribute
+ return jsonIgnore == null &&
+ xmlIgnore == null &&
+ ignoreDataMember == null &&
+ nonSerialized == null &&
+ (apiExplorerSetting == null || !apiExplorerSetting.IgnoreApi) &&
+ (!hasDataContractAttribute || hasMemberAttribute);
+ }
+
+ private string CreateDefaultDocumentation(Type type)
+ {
+ string documentation;
+ if (DefaultTypeDocumentation.TryGetValue(type, out documentation))
+ {
+ return documentation;
+ }
+ if (DocumentationProvider != null)
+ {
+ documentation = DocumentationProvider.GetDocumentation(type);
+ }
+
+ return documentation;
+ }
+
+ private void GenerateAnnotations(MemberInfo property, ParameterDescription propertyModel)
+ {
+ List annotations = new List();
+
+ IEnumerable attributes = property.GetCustomAttributes();
+ foreach (Attribute attribute in attributes)
+ {
+ Func