diff --git a/webapi/ClientApp/src/components/SideWidgets/Categories.tsx b/webapi/ClientApp/src/components/SideWidgets/Categories.tsx new file mode 100644 index 0000000..0f72def --- /dev/null +++ b/webapi/ClientApp/src/components/SideWidgets/Categories.tsx @@ -0,0 +1,39 @@ +import React, { FC } from 'react' +import { Card, CardBody, CardHeader, Col, Row } from 'reactstrap' +import { CategoryModel } from '../../models' + +interface ICategories { + items?: CategoryModel [] +} + +const Categories: FC = ({ + items = [] +}) => { + const middleIndex = Math.ceil(items.length / 2) + + const firstHalf = items.splice(0, middleIndex) + const secondHalf = items.splice(-middleIndex) + + return + Categories + + + + + + + + + + + + +} + +export { + Categories +} \ No newline at end of file diff --git a/webapi/ClientApp/src/components/SideWidgets/Empty.tsx b/webapi/ClientApp/src/components/SideWidgets/Empty.tsx new file mode 100644 index 0000000..d11a6df --- /dev/null +++ b/webapi/ClientApp/src/components/SideWidgets/Empty.tsx @@ -0,0 +1,15 @@ +import React, { FC } from 'react' +import { Card, CardBody, CardHeader } from 'reactstrap' + +const Empty : FC = () => { + return + + + + + +} + +export { + Empty +} \ No newline at end of file diff --git a/webapi/ClientApp/src/components/SideWidgets/Search.tsx b/webapi/ClientApp/src/components/SideWidgets/Search.tsx new file mode 100644 index 0000000..7735a20 --- /dev/null +++ b/webapi/ClientApp/src/components/SideWidgets/Search.tsx @@ -0,0 +1,18 @@ +import React, { FC } from 'react' +import { Card, CardBody, CardHeader } from 'reactstrap' + +const Search : FC = () => { + return + Search + +
+ + +
+
+
+} + +export { + Search +} \ No newline at end of file diff --git a/webapi/ClientApp/src/components/SideWidgets/index.tsx b/webapi/ClientApp/src/components/SideWidgets/index.tsx index 50905be..35abe6d 100644 --- a/webapi/ClientApp/src/components/SideWidgets/index.tsx +++ b/webapi/ClientApp/src/components/SideWidgets/index.tsx @@ -1,59 +1,14 @@ -import React, { FC } from 'react' -import { Card, CardBody, CardHeader, Col, Row } from 'reactstrap' -import { CategoryModel } from '../../models' +import { Search } from './Search' +import { Categories } from './Categories' +import { Empty } from './Empty' + -const Search = () => { - return - Search - -
- - -
-
-
-} -interface ICategories { - items?: CategoryModel [] -} -const Categories: FC = ({ - items = [] -}) => { - const middleIndex = Math.ceil(items.length / 2) - const firstHalf = items.splice(0, middleIndex) - const secondHalf = items.splice(-middleIndex) - return - Categories - - - - - - - - - - - -} -const Empty = () => { - return - Side Widget - - You can put anything you want inside of these side widgets. They are easy to use, and feature the Bootstrap 5 card component! - - -} export { Search, diff --git a/webapi/ClientApp/src/models/index.ts b/webapi/ClientApp/src/models/index.ts index ea3854f..f8475e4 100644 --- a/webapi/ClientApp/src/models/index.ts +++ b/webapi/ClientApp/src/models/index.ts @@ -40,6 +40,19 @@ export interface FormItemModel { placeHolder?: string, } +export interface HeaderLink { + [key: string]: string +} + +export interface Meta { + [key: string]: string +} +export interface HeaderModel { + title: string, + link: HeaderLink, + meta: Meta +} + export interface ImageModel { src: string, alt: string @@ -91,19 +104,3 @@ export interface TestimonialModel { text: string, reviewer: ReviewerModel } - - - - -export interface HeaderLink { - [key: string]: string -} - -export interface Meta { - [key: string]: string -} -export interface HeaderModel { - title: string, - link: HeaderLink, - meta: Meta -} \ No newline at end of file diff --git a/webapi/ClientApp/src/pages/Blog/Catalog/index.tsx b/webapi/ClientApp/src/pages/Blog/Catalog/index.tsx index c082958..76e5513 100644 --- a/webapi/ClientApp/src/pages/Blog/Catalog/index.tsx +++ b/webapi/ClientApp/src/pages/Blog/Catalog/index.tsx @@ -188,8 +188,8 @@ const BlogCatalog = () => { - - + {/* + */} diff --git a/webapi/ClientApp/src/pages/Blog/Item/index.tsx b/webapi/ClientApp/src/pages/Blog/Item/index.tsx index c5ed5c5..a562edd 100644 --- a/webapi/ClientApp/src/pages/Blog/Item/index.tsx +++ b/webapi/ClientApp/src/pages/Blog/Item/index.tsx @@ -84,7 +84,7 @@ const BlogItem = () => { if(blogItemTitle.postedOnBy && blogItem?.author) blogItemTitle.postedOnBy = blogItemTitle.postedOnBy?.replace('{nickName}', dateFormat(blogItem.author.nickName)) - return + return @@ -99,11 +99,7 @@ const BlogItem = () => { }} /> - - - - diff --git a/webapi/ClientApp/src/pages/Shop/Checkout/index.tsx b/webapi/ClientApp/src/pages/Shop/Checkout/index.tsx index 8406a3c..e0131fc 100644 --- a/webapi/ClientApp/src/pages/Shop/Checkout/index.tsx +++ b/webapi/ClientApp/src/pages/Shop/Checkout/index.tsx @@ -5,9 +5,9 @@ import { Container } from 'reactstrap' import style from './scss/style.module.scss' const Checkout = () => { - return + return
-
+

Checkout form

Below is an example form built entirely with Bootstrap’s form controls. Each required form group has a validation state that can be triggered by attempting to submit the form without completing it.

diff --git a/webapi/ClientApp/src/store/reducers/Content.ts b/webapi/ClientApp/src/store/reducers/Content.ts index 6a04df4..adadccd 100644 --- a/webapi/ClientApp/src/store/reducers/Content.ts +++ b/webapi/ClientApp/src/store/reducers/Content.ts @@ -469,6 +469,7 @@ const unloadedState: ContentState = { export const reducer: Reducer = (state: ContentState | undefined, incomingAction: Action): ContentState => { if (state === undefined) { + console.log(unloadedState) return unloadedState } diff --git a/webapi/Core/Abstractions/DomainObjects/DomainObject.cs b/webapi/Core/Abstractions/DomainObjects/DomainObjectBase.cs similarity index 72% rename from webapi/Core/Abstractions/DomainObjects/DomainObject.cs rename to webapi/Core/Abstractions/DomainObjects/DomainObjectBase.cs index f2e5d2a..dc72eb7 100644 --- a/webapi/Core/Abstractions/DomainObjects/DomainObject.cs +++ b/webapi/Core/Abstractions/DomainObjects/DomainObjectBase.cs @@ -5,6 +5,6 @@ using System.Text; using System.Threading.Tasks; namespace Core.Abstractions.DomainObjects { - public abstract class DomainObject { + public abstract class DomainObjectBase : EquatableBase { } } diff --git a/webapi/Core/Abstractions/DomainObjects/DomainObjectDocumentBase.cs b/webapi/Core/Abstractions/DomainObjects/DomainObjectDocumentBase.cs new file mode 100644 index 0000000..0df4bb8 --- /dev/null +++ b/webapi/Core/Abstractions/DomainObjects/DomainObjectDocumentBase.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Abstractions.DomainObjects { + public abstract class DomainObjectDocumentBase : DomainObjectBase { + public Guid Id { get; set; } + + } +} diff --git a/webapi/Core/Abstractions/DomainObjects/PageBase.cs b/webapi/Core/Abstractions/DomainObjects/PageBase.cs new file mode 100644 index 0000000..ec2dee6 --- /dev/null +++ b/webapi/Core/Abstractions/DomainObjects/PageBase.cs @@ -0,0 +1,5 @@ + + +namespace Core.Abstractions.DomainObjects { + public abstract class PageBase : DomainObjectBase { } +} diff --git a/webapi/Core/Abstractions/DomainObjects/PageSectionBase.cs b/webapi/Core/Abstractions/DomainObjects/PageSectionBase.cs new file mode 100644 index 0000000..0277827 --- /dev/null +++ b/webapi/Core/Abstractions/DomainObjects/PageSectionBase.cs @@ -0,0 +1,7 @@ + +namespace Core.Abstractions.DomainObjects { + public abstract class PageSectionBase : DomainObjectBase { + public string? Title { get; set; } + public string? Text { get; set; } + } +} diff --git a/webapi/Core/Abstractions/DomainObjects/PersonBase.cs b/webapi/Core/Abstractions/DomainObjects/PersonBase.cs new file mode 100644 index 0000000..aebfd55 --- /dev/null +++ b/webapi/Core/Abstractions/DomainObjects/PersonBase.cs @@ -0,0 +1,8 @@ +using Core.DomainObjects; + +namespace Core.Abstractions.DomainObjects { + public abstract class PersonBase : DomainObjectBase { + public Guid Id { get; set; } + public Image? Image { get; set; } + } +} diff --git a/webapi/Core/Abstractions/DomainObjects/PostItem.cs b/webapi/Core/Abstractions/DomainObjects/PostItemBase.cs similarity index 89% rename from webapi/Core/Abstractions/DomainObjects/PostItem.cs rename to webapi/Core/Abstractions/DomainObjects/PostItemBase.cs index 7198403..c0799f3 100644 --- a/webapi/Core/Abstractions/DomainObjects/PostItem.cs +++ b/webapi/Core/Abstractions/DomainObjects/PostItemBase.cs @@ -7,8 +7,7 @@ using System.Threading.Tasks; namespace Core.Abstractions.DomainObjects { - public abstract class PostItem : DomainObject { - public Guid Id { get; set; } + public abstract class PostItemBase : DomainObjectDocumentBase { /// /// Author / Owner diff --git a/webapi/Core/Abstractions/Enumeration.cs b/webapi/Core/Abstractions/Enumeration.cs new file mode 100644 index 0000000..a07adbe --- /dev/null +++ b/webapi/Core/Abstractions/Enumeration.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Core.Abstractions { + public abstract class Enumeration : IComparable { + public string Name { get; private set; } + + public int Id { get; private set; } + + protected Enumeration(int id, string name) => (Id, Name) = (id, name); + + public override string ToString() => Name; + + public static IEnumerable GetAll() where T : Enumeration => + typeof(T).GetFields(BindingFlags.Public | + BindingFlags.Static | + BindingFlags.DeclaredOnly) + .Select(f => f.GetValue(null)) + .Cast(); + + public override bool Equals(object? obj) { + if (obj == null) + return false; + + if (obj is not Enumeration otherValue) { + return false; + } + + var typeMatches = GetType().Equals(obj.GetType()); + var valueMatches = Id.Equals(otherValue.Id); + + return typeMatches && valueMatches; + } + + public override int GetHashCode() => Id.GetHashCode(); + + public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue) { + var absoluteDifference = Math.Abs(firstValue.Id - secondValue.Id); + return absoluteDifference; + } + + public static T FromValue(int value) where T : Enumeration { + var matchingItem = Parse(value, "value", item => item.Id == value); + return matchingItem; + } + + public static T FromDisplayName(string displayName) where T : Enumeration { + var matchingItem = Parse(displayName, "display name", item => item.Name == displayName); + return matchingItem; + } + + private static T Parse(K value, string description, Func predicate) where T : Enumeration { + var matchingItem = GetAll().FirstOrDefault(predicate); + + if (matchingItem == null) + throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}"); + + return matchingItem; + } + + public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id); + } +} \ No newline at end of file diff --git a/webapi/Core/Abstractions/EquatableBase.cs b/webapi/Core/Abstractions/EquatableBase.cs new file mode 100644 index 0000000..044c96c --- /dev/null +++ b/webapi/Core/Abstractions/EquatableBase.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Abstractions { + public abstract class EquatableBase : IEquatable { + public bool Equals(T? other) { + if (other == null) + return false; + + return GetHashCode() == other.GetHashCode(); + } + + public abstract override int GetHashCode(); + } +} diff --git a/webapi/Core/DomainObjects/Author.cs b/webapi/Core/DomainObjects/Author.cs new file mode 100644 index 0000000..6dbc752 --- /dev/null +++ b/webapi/Core/DomainObjects/Author.cs @@ -0,0 +1,16 @@ +using Core.Abstractions.DomainObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects { + public class Author : PersonBase{ + public string NickName { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/BlogItem.cs b/webapi/Core/DomainObjects/BlogItem.cs index f550290..40158d1 100644 --- a/webapi/Core/DomainObjects/BlogItem.cs +++ b/webapi/Core/DomainObjects/BlogItem.cs @@ -1,9 +1,14 @@ using Core.Abstractions.DomainObjects; namespace Core.DomainObjects { - internal class BlogItem : PostItem { + public class BlogItem : DomainObjectBase { - public int Likes { get; set; } - public int ReadingTime { get; set; } + public int? ReadTime { get; set; } + + public int? Likes { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } } } diff --git a/webapi/Core/DomainObjects/Category.cs b/webapi/Core/DomainObjects/Category.cs new file mode 100644 index 0000000..86634c0 --- /dev/null +++ b/webapi/Core/DomainObjects/Category.cs @@ -0,0 +1,13 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects { + public class Category : DomainObjectBase { + + public Guid Id { get; set; } + public string Text { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Comment.cs b/webapi/Core/DomainObjects/Comment.cs new file mode 100644 index 0000000..3b86933 --- /dev/null +++ b/webapi/Core/DomainObjects/Comment.cs @@ -0,0 +1,18 @@ +using Core.Abstractions.DomainObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects { + public class Comment : DomainObjectBase { + public Author Author { get; set; } + public string Text { get; set; } + public List? Responses { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Documents/BlogItem.cs b/webapi/Core/DomainObjects/Documents/BlogItem.cs new file mode 100644 index 0000000..765f25f --- /dev/null +++ b/webapi/Core/DomainObjects/Documents/BlogItem.cs @@ -0,0 +1,13 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects.Documents { + public class BlogItem : PostItemBase { + + public int Likes { get; set; } + public int ReadingTime { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Documents/Content.cs b/webapi/Core/DomainObjects/Documents/Content.cs new file mode 100644 index 0000000..97f3eee --- /dev/null +++ b/webapi/Core/DomainObjects/Documents/Content.cs @@ -0,0 +1,34 @@ +using Core.Abstractions.DomainObjects; +using Core.DomainObjects.Pages; + +namespace Core.DomainObjects.Documents { + + public class Content : DomainObjectDocumentBase { + + public string SiteName { get; set; } + public string SiteUrl { get; set; } + + public Header Header { get; set; } + + public Localization Localization { get; set; } + + public List Routes { get; set; } + public List AdminRoutes { get; set; } + public List ServiceRoutes { get; set; } + + public List TopMenu { get; set; } + public List SideMenu { get; set; } + + public HomePage HomePage { get; set; } + + public ShopCatalogPage ShopCatalog { get; set; } + public ShopItemPage ShopItem { get; set; } + + public BlogCatalogPage BlogCatalog { get; set; } + public BlogItemPage Blogitem { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Documents/ShopItem.cs b/webapi/Core/DomainObjects/Documents/ShopItem.cs new file mode 100644 index 0000000..a28fa3d --- /dev/null +++ b/webapi/Core/DomainObjects/Documents/ShopItem.cs @@ -0,0 +1,15 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects.Documents { + public class ShopItem : PostItemBase { + public string Sku { get; set; } + public int Rating { get; set; } + public int Price { get; set; } + public int NewPrice { get; set; } + public int Quantity { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/User.cs b/webapi/Core/DomainObjects/Documents/User.cs similarity index 81% rename from webapi/Core/DomainObjects/User.cs rename to webapi/Core/DomainObjects/Documents/User.cs index 527a2be..d8ef908 100644 --- a/webapi/Core/DomainObjects/User.cs +++ b/webapi/Core/DomainObjects/Documents/User.cs @@ -9,9 +9,7 @@ using System.Threading.Tasks; namespace Core.DomainObjects { - public class User : DomainObject { - public Guid Id { get; set; } - + public class User : DomainObjectDocumentBase { public string NickName { get; set; } public string Hash { get; set; } @@ -25,5 +23,9 @@ namespace Core.DomainObjects { public Address BillingAddress { get; set; } public Address ShippingAddress { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } } } diff --git a/webapi/Core/DomainObjects/Feature.cs b/webapi/Core/DomainObjects/Feature.cs new file mode 100644 index 0000000..975a4bb --- /dev/null +++ b/webapi/Core/DomainObjects/Feature.cs @@ -0,0 +1,13 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects { + public class Feature : DomainObjectBase { + public string Icon { get; set; } + public string Title { get; set; } + public string Text { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/FormItem.cs b/webapi/Core/DomainObjects/FormItem.cs new file mode 100644 index 0000000..f175ae1 --- /dev/null +++ b/webapi/Core/DomainObjects/FormItem.cs @@ -0,0 +1,17 @@ +using Core.Abstractions.DomainObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects { + public class FormItem : DomainObjectBase { + public string? Title { get; set; } + public string? PlaceHolder { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Header.cs b/webapi/Core/DomainObjects/Header.cs new file mode 100644 index 0000000..f5444e7 --- /dev/null +++ b/webapi/Core/DomainObjects/Header.cs @@ -0,0 +1,16 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects { + public class Header : DomainObjectBase
{ + + public string Title { get; set; } + + public Dictionary HeaderLink { get; set; } + + public Dictionary Meta { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Image.cs b/webapi/Core/DomainObjects/Image.cs new file mode 100644 index 0000000..01aaf2c --- /dev/null +++ b/webapi/Core/DomainObjects/Image.cs @@ -0,0 +1,12 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects { + public class Image : DomainObjectBase { + public string Src { get; set; } + public string Alt { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Link.cs b/webapi/Core/DomainObjects/Link.cs new file mode 100644 index 0000000..41a75a1 --- /dev/null +++ b/webapi/Core/DomainObjects/Link.cs @@ -0,0 +1,12 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects { + public class Link : DomainObjectBase { + public string Target { get; set; } + public string AnchorText { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Localization.cs b/webapi/Core/DomainObjects/Localization.cs new file mode 100644 index 0000000..dd6c3f4 --- /dev/null +++ b/webapi/Core/DomainObjects/Localization.cs @@ -0,0 +1,27 @@ +using Core.Abstractions.DomainObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects { + public class Localization : DomainObjectBase { + + public string TimeZone { get; set; } + + public string Locale { get; set; } + + public string DateFormat { get; set; } + + public string TimeFormat { get; set; } + + public string Currency { get; set; } + + public string CurrencySymobol { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/MenuItem.cs b/webapi/Core/DomainObjects/MenuItem.cs new file mode 100644 index 0000000..afc09e6 --- /dev/null +++ b/webapi/Core/DomainObjects/MenuItem.cs @@ -0,0 +1,15 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects { + public class MenuItem : DomainObjectBase { + + public string? Icon { get; set; } + public string? Title { get; set; } + public string? Target { get; set; } + public List? ChildItems { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/PageSections/BlogTitleSection.cs b/webapi/Core/DomainObjects/PageSections/BlogTitleSection.cs new file mode 100644 index 0000000..64879c0 --- /dev/null +++ b/webapi/Core/DomainObjects/PageSections/BlogTitleSection.cs @@ -0,0 +1,11 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects.PageSections { + public class BlogTitleSection : PageSectionBase { + public string PostedOnBy { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/PageSections/CallToActionSection.cs b/webapi/Core/DomainObjects/PageSections/CallToActionSection.cs new file mode 100644 index 0000000..e7c334a --- /dev/null +++ b/webapi/Core/DomainObjects/PageSections/CallToActionSection.cs @@ -0,0 +1,14 @@ +using Core.Abstractions.DomainObjects; + + +namespace Core.DomainObjects.PageSections { + public class CallToActionSection : PageSectionBase { + public string PrivacyDisclaimer { get; set; } + + public FormItem Email { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/PageSections/CommentsSection.cs b/webapi/Core/DomainObjects/PageSections/CommentsSection.cs new file mode 100644 index 0000000..7f78a5b --- /dev/null +++ b/webapi/Core/DomainObjects/PageSections/CommentsSection.cs @@ -0,0 +1,16 @@ +using Core.Abstractions.DomainObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects.PageSections { + public class CommentsSection : PageSectionBase { + public string LeaveComment { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/PageSections/FeaturedBlogSection.cs b/webapi/Core/DomainObjects/PageSections/FeaturedBlogSection.cs new file mode 100644 index 0000000..ac92c79 --- /dev/null +++ b/webapi/Core/DomainObjects/PageSections/FeaturedBlogSection.cs @@ -0,0 +1,16 @@ +using Core.Abstractions.DomainObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects.PageSections { + public class FeaturedBlogSection : PageSectionBase { + public string ReadTime { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/PageSections/FeaturedBlogsSection.cs b/webapi/Core/DomainObjects/PageSections/FeaturedBlogsSection.cs new file mode 100644 index 0000000..2c90424 --- /dev/null +++ b/webapi/Core/DomainObjects/PageSections/FeaturedBlogsSection.cs @@ -0,0 +1,15 @@ +using Core.Abstractions.DomainObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects.PageSections { + public class FeaturedBologsSection : PageSectionBase { + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/PageSections/FeaturesSection.cs b/webapi/Core/DomainObjects/PageSections/FeaturesSection.cs new file mode 100644 index 0000000..12a25e6 --- /dev/null +++ b/webapi/Core/DomainObjects/PageSections/FeaturesSection.cs @@ -0,0 +1,11 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects.PageSections { + public class FeaturesSection : PageSectionBase { + public List Items { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/PageSections/ProductSection.cs b/webapi/Core/DomainObjects/PageSections/ProductSection.cs new file mode 100644 index 0000000..70b3817 --- /dev/null +++ b/webapi/Core/DomainObjects/PageSections/ProductSection.cs @@ -0,0 +1,17 @@ +using Core.Abstractions.DomainObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects.PageSections { + public class ProductSection : PageSectionBase { + public string AvailableQuantity { get; set; } + public string AddToCart { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/PageSections/RelatedProductsSection.cs b/webapi/Core/DomainObjects/PageSections/RelatedProductsSection.cs new file mode 100644 index 0000000..f2318ad --- /dev/null +++ b/webapi/Core/DomainObjects/PageSections/RelatedProductsSection.cs @@ -0,0 +1,15 @@ +using Core.Abstractions.DomainObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects.PageSections { + public class RelatedProductsSection : PageSectionBase { + public override int GetHashCode() { + throw new NotImplementedException(); + } + + } +} diff --git a/webapi/Core/DomainObjects/PageSections/TestimonialsSection.cs b/webapi/Core/DomainObjects/PageSections/TestimonialsSection.cs new file mode 100644 index 0000000..471d20b --- /dev/null +++ b/webapi/Core/DomainObjects/PageSections/TestimonialsSection.cs @@ -0,0 +1,10 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects.PageSections { + public class TestimonialsSection : PageSectionBase { + public List Items { get; set; } + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/PageSections/TitleSection.cs b/webapi/Core/DomainObjects/PageSections/TitleSection.cs new file mode 100644 index 0000000..ec3c5df --- /dev/null +++ b/webapi/Core/DomainObjects/PageSections/TitleSection.cs @@ -0,0 +1,13 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects.PageSections { + public class TitleSection : PageSectionBase { + public Image? Image { get; set; } + public MenuItem? PrimaryLink { get; set; } + public MenuItem? SecondaryLink { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Pages/BlogCatalogPage.cs b/webapi/Core/DomainObjects/Pages/BlogCatalogPage.cs new file mode 100644 index 0000000..2ad7f0b --- /dev/null +++ b/webapi/Core/DomainObjects/Pages/BlogCatalogPage.cs @@ -0,0 +1,14 @@ +using Core.Abstractions.DomainObjects; +using Core.DomainObjects.PageSections; + +namespace Core.DomainObjects.Pages { + public class BlogCatalogPage : PageBase { + + public TitleSection TitleSection { get; set; } + public FeaturedBlogSection FeaturedBlogSection { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Pages/BlogItemPage.cs b/webapi/Core/DomainObjects/Pages/BlogItemPage.cs new file mode 100644 index 0000000..08e437a --- /dev/null +++ b/webapi/Core/DomainObjects/Pages/BlogItemPage.cs @@ -0,0 +1,13 @@ +using Core.Abstractions.DomainObjects; +using Core.DomainObjects.PageSections; + +namespace Core.DomainObjects.Pages { + public class BlogItemPage : PageBase { + public BlogTitleSection TitleSection { get; set; } + public CommentsSection CommentsSection { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Pages/HomePage.cs b/webapi/Core/DomainObjects/Pages/HomePage.cs new file mode 100644 index 0000000..926e4f0 --- /dev/null +++ b/webapi/Core/DomainObjects/Pages/HomePage.cs @@ -0,0 +1,17 @@ +using Core.Abstractions.DomainObjects; +using Core.DomainObjects.PageSections; + +namespace Core.DomainObjects.Pages { + public class HomePage : PageBase { + + public TitleSection TitleSection { get; set; } + public FeaturesSection FeaturesSection { get; set; } + public TestimonialsSection TestimonialsSection { get; set; } + public FeaturedBologsSection FeaturedBlogsSection { get; set; } + public CallToActionSection CallToActionSection { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Pages/ShopCatalogPage.cs b/webapi/Core/DomainObjects/Pages/ShopCatalogPage.cs new file mode 100644 index 0000000..b516824 --- /dev/null +++ b/webapi/Core/DomainObjects/Pages/ShopCatalogPage.cs @@ -0,0 +1,12 @@ +using Core.Abstractions.DomainObjects; +using Core.DomainObjects.PageSections; + +namespace Core.DomainObjects.Pages { + public class ShopCatalogPage : PageBase { + public TitleSection TitleSection { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Pages/ShopItemPage.cs b/webapi/Core/DomainObjects/Pages/ShopItemPage.cs new file mode 100644 index 0000000..36da1b7 --- /dev/null +++ b/webapi/Core/DomainObjects/Pages/ShopItemPage.cs @@ -0,0 +1,15 @@ +using Core.Abstractions.DomainObjects; +using Core.DomainObjects.PageSections; + +namespace Core.DomainObjects.Pages { + public class ShopItemPage : PageBase { + + public ProductSection ProductSection { get; set; } + + public RelatedProductsSection RelatedProductsSection { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Reviewer.cs b/webapi/Core/DomainObjects/Reviewer.cs new file mode 100644 index 0000000..8ce38a8 --- /dev/null +++ b/webapi/Core/DomainObjects/Reviewer.cs @@ -0,0 +1,12 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects { + public class Reviewer : PersonBase { + public string FullName { get; set; } + public string Position { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/Route.cs b/webapi/Core/DomainObjects/Route.cs new file mode 100644 index 0000000..2400621 --- /dev/null +++ b/webapi/Core/DomainObjects/Route.cs @@ -0,0 +1,14 @@ +using Core.Abstractions.DomainObjects; + +namespace Core.DomainObjects { + public class Route : DomainObjectBase { + + public string Target { get; set; } + public string? Component { get; set; } + public List? ChildRoutes { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/Core/DomainObjects/ShopItem.cs b/webapi/Core/DomainObjects/ShopItem.cs deleted file mode 100644 index a5f9518..0000000 --- a/webapi/Core/DomainObjects/ShopItem.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Core.Abstractions; -using Core.Abstractions.DomainObjects; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Core.DomainObjects { - internal class ShopItem : PostItem { - public string Sku { get; set; } - public int Rating { get; set; } - public int Price { get; set; } - public int NewPrice { get; set; } - public int Quantity { get; set; } - } -} diff --git a/webapi/Core/DomainObjects/Testimonial.cs b/webapi/Core/DomainObjects/Testimonial.cs new file mode 100644 index 0000000..5222758 --- /dev/null +++ b/webapi/Core/DomainObjects/Testimonial.cs @@ -0,0 +1,17 @@ +using Core.Abstractions.DomainObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects { + public class Testimonial : DomainObjectBase { + public string Text { get; set; } + public Reviewer Reviewer { get; set; } + + public override int GetHashCode() { + throw new NotImplementedException(); + } + } +} diff --git a/webapi/DataProviders/Abstractions/DataProviderBase.cs b/webapi/DataProviders/Abstractions/DataProviderBase.cs new file mode 100644 index 0000000..67c73f9 --- /dev/null +++ b/webapi/DataProviders/Abstractions/DataProviderBase.cs @@ -0,0 +1,277 @@ +using System.Linq.Expressions; + +using Microsoft.Extensions.Logging; + +using MongoDB.Bson.Serialization; +using MongoDB.Driver; + +using DomainResults.Common; + +using Core.Abstractions.DomainObjects; + +namespace DataProviders.Abstractions { + public abstract class DataProviderBase where T : DomainObjectDocumentBase { + + private protected const string _databaseName = "reactredux"; + + private protected readonly ILogger> _logger; + private protected readonly IMongoClient _client; + private protected readonly IIdGenerator _idGenerator; + private protected readonly ISessionService _sessionService; + + private protected List? _collection; + + /// + /// Main constructor + /// + /// + /// + /// + /// + public DataProviderBase( + ILogger> logger, + IMongoClient client, + IIdGenerator idGenerator, + ISessionService sessionService + ) { + _logger = logger; + _client = client; + _idGenerator = idGenerator; + _sessionService = sessionService; + } + + /// + /// Testing constructor + /// + /// + /// + public DataProviderBase( + ILogger> logger, + ISessionService sessionService, + List collection + ) { + _logger = logger; + _sessionService = sessionService; + _collection = collection ?? new List(); + } + + #region Insert + private protected (Guid?, IDomainResult) Insert(T obj, string collectionName) => + InsertAsync(obj, collectionName).Result; + + private protected (Guid?, IDomainResult) Insert(T obj, string collectionName, Guid sessionId) => + InsertAsync(obj, collectionName, sessionId).Result; + + private protected Task<(Guid?, IDomainResult)> InsertAsync(T obj, string collectionName) => + InsertAsyncCore(obj, collectionName, null); + + private protected Task<(Guid?, IDomainResult)> InsertAsync(T obj, string collectionName, Guid sessionId) => + InsertAsyncCore(obj, collectionName, sessionId); + #endregion + + #region InsertMany + private protected (List?, IDomainResult) InsertMany(List objList, string collectionName) => + InsertManyAsync(objList, collectionName).Result; + + private protected (List?, IDomainResult) InsertMany(List objList, string collectionName, Guid sessionId) => + InsertManyAsync(objList, collectionName, sessionId).Result; + + private protected Task<(List?, IDomainResult)> InsertManyAsync(List objList, string collectionName) => + InsertManyAsyncCore(objList, collectionName, null); + + private protected Task<(List?, IDomainResult)> InsertManyAsync(List objList, string collectionName, Guid sessionId) => + InsertManyAsyncCore(objList, collectionName, sessionId); + #endregion + + #region Get + private protected (List?, IDomainResult) GetWithPredicate(Expression> predicate, string collectionName) => + GetWithPredicateCore(predicate, collectionName); + #endregion + + #region Update + private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression> predicate, string collectionName) => + UpdateWithPredicateAsync(obj, predicate, collectionName).Result; + + private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression> predicate, string collectionName, Guid sessionId) => + UpdateWithPredicateAsync(obj, predicate, collectionName, sessionId).Result; + + private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression> predicate, string collectionName) => + UpdateWithPredicateAsyncCore(obj, predicate, collectionName, null); + + private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression> predicate, string collectionName, Guid sessionId) => + UpdateWithPredicateAsyncCore(obj, predicate, collectionName, sessionId); + #endregion + + #region Exists + private protected (Guid?, IDomainResult) Exists(Guid id, string collectionName) { + var (_resultList, result) = GetWithPredicate(x => x.Id == id, collectionName); + + return (result.Status != DomainOperationStatus.Failed && _resultList != null && _resultList.Count > 0 + ? id + :null, + result); + } + #endregion + + #region Delete + private protected IDomainResult DeleteWithPredicate(Expression> predicate, string collectionName) => + DeleteWithPredicateAsync(predicate, collectionName).Result; + + private protected IDomainResult DeleteWithPredicate(Expression> predicate, string collectionName, Guid sessionId) => + DeleteWithPredicateAsync(predicate, collectionName, sessionId).Result; + + private protected Task DeleteWithPredicateAsync(Expression> predicate, string collectionName) => + DeleteWithPredicateAsyncCore(predicate, collectionName, null); + + private protected Task DeleteWithPredicateAsync(Expression> predicate, string collectionName, Guid sessionId) => + DeleteWithPredicateAsyncCore(predicate, collectionName, sessionId); + #endregion + + + #region DeleteMany + private protected IDomainResult DeleteManyWithPredicate(Expression> predicate, string collectionName) => + DeleteManyWithPredicateAsync(predicate, collectionName).Result; + + private protected IDomainResult DeleteManyWithPredicate(Expression> predicate, string collectionName, Guid sessionId) => + DeleteManyWithPredicateAsync(predicate, collectionName, sessionId).Result; + + private protected Task DeleteManyWithPredicateAsync(Expression> predicate, string collectionName) => + DeleteManyWithPredicateAsyncCore(predicate, collectionName, null); + + private protected Task DeleteManyWithPredicateAsync(Expression> predicate, string collectionName, Guid sessionId) => + DeleteManyWithPredicateAsyncCore(predicate, collectionName, sessionId); + #endregion + + + #region Core methods + private async protected Task<(Guid?, IDomainResult)> InsertAsyncCore(T obj, string collectionName, Guid? sessionId) { + try { + if (_collection != null) { + obj.Id = Guid.NewGuid(); + _collection.Add(obj); + return IDomainResult.Success(obj.Id); + } + + var collection = _client.GetDatabase(_databaseName).GetCollection(collectionName); + + if (sessionId != null) + await collection.InsertOneAsync(_sessionService.GetSession(sessionId.Value), obj); + else + collection.InsertOne(obj); + + return IDomainResult.Success(obj.Id); + } + catch (Exception ex) { + _logger.LogError(ex, "Data provider error"); + return IDomainResult.Failed(); + } + } + + private async Task<(List?, IDomainResult)> InsertManyAsyncCore(List objList, string collectionName, Guid? sessionId) { + try { + if (_collection != null) { + _collection = _collection.Concat(objList).ToList(); + return IDomainResult.Success(objList.Select(x => x.Id).ToList()); + } + + var collection = _client.GetDatabase(_databaseName).GetCollection(collectionName); + + if (sessionId != null) + await collection.InsertManyAsync(_sessionService.GetSession(sessionId.Value), objList); + else + collection.InsertMany(objList); + + return IDomainResult.Success(objList.Select(x => x.Id).ToList()); + } + catch (Exception ex) { + _logger.LogError(ex, "Data provider error"); + return IDomainResult.Failed?>(); + } + } + + private (List?, IDomainResult) GetWithPredicateCore(Expression> predicate, string collectionName) { + try { + List? result; + + if (_collection != null) { + result = _collection?.AsQueryable() + .Where(predicate).ToList(); + } + else { + result = _client.GetDatabase(_databaseName).GetCollection(collectionName) + .Find(predicate).ToList(); + } + + return result != null + ? IDomainResult.Success(result) + : IDomainResult.NotFound?>(); + } + catch (Exception ex) { + _logger.LogError(ex, "Data provider error"); + return IDomainResult.Failed?>(); + } + } + + private async Task<(Guid?, IDomainResult)> UpdateWithPredicateAsyncCore(T obj, Expression> predicate, string collectionName, Guid? sessionId) { + try { + if (_collection != null) { + // remove element(s) from list + foreach (var element in _collection.AsQueryable().Where(predicate)) + _collection = _collection.Where(x => x.Id != element.Id).ToList(); + + // add updated element + _collection.Add(obj); + } + else { + var collection = _client.GetDatabase(_databaseName).GetCollection(collectionName); + + if (sessionId != null) + await collection.ReplaceOneAsync(_sessionService.GetSession(sessionId.Value), predicate, obj); + else + await collection.ReplaceOneAsync(predicate, obj); + } + + return IDomainResult.Success(obj.Id); + } + catch (Exception ex) { + _logger.LogError(ex, "Data provider error"); + return IDomainResult.Failed(); + } + } + + private async Task DeleteWithPredicateAsyncCore(Expression> predicate, string collectionName, Guid? sessionId) { + try { + var collection = _client.GetDatabase(_databaseName).GetCollection(collectionName); + + if (sessionId != null) + await collection.DeleteOneAsync(_sessionService.GetSession(sessionId.Value), predicate); + else + await collection.DeleteOneAsync(predicate); + + return IDomainResult.Success(); + } + catch (Exception ex) { + _logger.LogError(ex, "Data provider error"); + return IDomainResult.Failed(); + } + } + + private async Task DeleteManyWithPredicateAsyncCore(Expression> predicate, string collectionName, Guid? sessionId) { + try { + var collection = _client.GetDatabase(_databaseName).GetCollection(collectionName); + + if (sessionId != null) + await collection.DeleteManyAsync(_sessionService.GetSession(sessionId.Value), predicate); + else + await collection.DeleteManyAsync(predicate); + + return IDomainResult.Success(); + } + catch (Exception ex) { + _logger.LogError(ex, "Data provider error"); + return IDomainResult.Failed(); + } + } + #endregion + } +} diff --git a/webapi/DataProviders/BlogDataProvider.cs b/webapi/DataProviders/BlogDataProvider.cs index 413bbc1..3a0c40d 100644 --- a/webapi/DataProviders/BlogDataProvider.cs +++ b/webapi/DataProviders/BlogDataProvider.cs @@ -4,10 +4,10 @@ public class BlogDataProvider : IBlogDataProvider { - private readonly IDataProvidersConfiguration _configuration; - public BlogDataProvider(IDataProvidersConfiguration configuration) { - _configuration = configuration; + + public BlogDataProvider() { + } } diff --git a/webapi/DataProviders/ContentDataProvider.cs b/webapi/DataProviders/ContentDataProvider.cs new file mode 100644 index 0000000..78cd1cd --- /dev/null +++ b/webapi/DataProviders/ContentDataProvider.cs @@ -0,0 +1,39 @@ +using Core.DomainObjects.Documents; +using DataProviders.Abstractions; +using DomainResults.Common; +using Microsoft.Extensions.Logging; +using MongoDB.Bson.Serialization; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DataProviders { + + public interface IContentDataProvider { + (Content?, IDomainResult) Get(Guid id); + } + + + public class ContentDataProvider : DataProviderBase, IContentDataProvider { + + private const string _collectionName = "contents"; + public ContentDataProvider( + ILogger> logger, + IMongoClient client, + IIdGenerator idGenerator, + ISessionService sessionService) : base(logger, client, idGenerator, sessionService) { + } + + public (Content?, IDomainResult) Get(Guid id) { + var (list, result) = GetWithPredicate(x => x.Id == id, _collectionName); + + if (!result.IsSuccess || list == null) + return (null, result); + + return (list.First(), result); + } + } +} diff --git a/webapi/DataProviders/Converters/EnumerationSerializer.cs b/webapi/DataProviders/Converters/EnumerationSerializer.cs new file mode 100644 index 0000000..9726cba --- /dev/null +++ b/webapi/DataProviders/Converters/EnumerationSerializer.cs @@ -0,0 +1,72 @@ +using MongoDB.Bson; +using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; +using Core.Abstractions; + +namespace PecMgr.DataProviders.Converters { + + public class EnumerationSerializer : IBsonSerializer where T : Enumeration { + private T Deserialize(IBsonReader reader) => Enumeration.FromValue(BsonSerializer.Deserialize(reader)); + private void Serialize(IBsonWriter writer, T value) => BsonSerializer.Serialize(writer, value.Id); + + public Type ValueType { get => typeof(T); } + + public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => Deserialize(context.Reader); + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) => Serialize(context.Writer, (T)value); + + + T IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => Deserialize(context.Reader); + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, T value) => Serialize(context.Writer, value); + } + + public class EnumerationListSerializer : IBsonSerializer>, IBsonArraySerializer where T : Enumeration { + private List Deserialize(IBsonReader reader) { + var type = reader.GetCurrentBsonType(); + var response = new List(); + + switch (type) { + case BsonType.Array: + reader.ReadStartArray(); + while (reader.ReadBsonType() != BsonType.EndOfDocument) { + response.Add(Enumeration.FromValue(BsonSerializer.Deserialize(reader))); + } + + reader.ReadEndArray(); + return response; + + default: + throw new NotImplementedException($"No implementation to deserialize {type}"); + } + + return response; + } + private void Serialize(IBsonWriter writer, List values) { + if (values != null) { + writer.WriteStartArray(); + + foreach (var value in values) { + writer.WriteInt32(value.Id); + } + + writer.WriteEndArray(); + } + + } + + public Type ValueType { get => typeof(List); } + + public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => Deserialize(context.Reader); + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) => Serialize(context.Writer, (List)value); + + List IBsonSerializer>.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => Deserialize(context.Reader); + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, List value) => Serialize(context.Writer, value); + + public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo) { + string elementName = null; + var serializer = BsonSerializer.LookupSerializer(typeof(string)); + var nominalType = typeof(string); + serializationInfo = new BsonSerializationInfo(elementName, serializer, nominalType); + return true; + } + } +} diff --git a/webapi/DataProviders/DataProviders.csproj b/webapi/DataProviders/DataProviders.csproj index 132c02c..1de6c3d 100644 --- a/webapi/DataProviders/DataProviders.csproj +++ b/webapi/DataProviders/DataProviders.csproj @@ -6,4 +6,15 @@ enable + + + + + + + + + + + diff --git a/webapi/DataProviders/Extensions/ServiceCollectionExtensions.cs b/webapi/DataProviders/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..b02c6c3 --- /dev/null +++ b/webapi/DataProviders/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; + +using MongoDB.Driver; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.IdGenerators; + +namespace DataProviders.Extensions +{ + public static class ServiceCollectionExtensions { + public static void RegisterDataproviders(this IServiceCollection services, IDataProvidersConfig appSettings) { + var config = appSettings.Database; + + services.AddSingleton(x => new MongoClient(config.ConnectionString)); + services.AddSingleton(); + + services.AddSingleton(); + + services.AddSingleton(); + + Mappings.RegisterClassMap(); + } + } +} diff --git a/webapi/DataProviders/IDataProvidersConfig.cs b/webapi/DataProviders/IDataProvidersConfig.cs new file mode 100644 index 0000000..dd47fd8 --- /dev/null +++ b/webapi/DataProviders/IDataProvidersConfig.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DataProviders { + public interface IDataProvidersConfig { + public Database Database { get; set; } + } + + public interface IDatabase { + + string ConnectionString { get; set; } + } + + public class Database : IDatabase { + private string _connectionString; + public string ConnectionString { + get { + var envVar = Environment.GetEnvironmentVariable("DB_CONN"); + return envVar ?? _connectionString; + } + + set => _connectionString = value; + } + } +} diff --git a/webapi/DataProviders/IDataProvidersConfiguration.cs b/webapi/DataProviders/IDataProvidersConfiguration.cs deleted file mode 100644 index 01db840..0000000 --- a/webapi/DataProviders/IDataProvidersConfiguration.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DataProviders { - public interface IDataProvidersConfiguration { - public Database Database { get; set; } - } - - public class Database { - public string ConnectionString { get; set; } - } -} diff --git a/webapi/DataProviders/Mappings.cs b/webapi/DataProviders/Mappings.cs new file mode 100644 index 0000000..e6bd386 --- /dev/null +++ b/webapi/DataProviders/Mappings.cs @@ -0,0 +1,24 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Conventions; +using MongoDB.Bson.Serialization.Serializers; + +using DataProviders; + +namespace DataProviders { + public class Mappings { + + public static void RegisterClassMap() { + ConventionRegistry.Register("MyConventions", + new ConventionPack { + new CamelCaseElementNameConvention(), + new IgnoreIfNullConvention(true) + }, type => true); + + // https://kevsoft.net/2020/06/25/storing-guids-as-strings-in-mongodb-with-csharp.html + BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String)); + + + } + } +} diff --git a/webapi/DataProviders/SessionService.cs b/webapi/DataProviders/SessionService.cs new file mode 100644 index 0000000..a8ad61c --- /dev/null +++ b/webapi/DataProviders/SessionService.cs @@ -0,0 +1,83 @@ +using Microsoft.Extensions.Logging; + +using MongoDB.Driver; + +namespace DataProviders { + + public interface ISessionService { + Task StartSession(); + IClientSessionHandle? GetSession(Guid id); + void StartTransaction(Guid id); + void CommitTransaction(Guid id); + void RollbackTransaction(Guid id); + void DisposeSession(Guid id); + } + public class SessionService : ISessionService { + + private readonly ILogger _logger; + private readonly IMongoClient _client; + private Dictionary _sessions; + + public SessionService( + ILogger logger, + IMongoClient client + ) { + _logger = logger; + _client = client; + _sessions = new Dictionary(); + } + + public async Task StartSession() { + var sessionId = Guid.NewGuid(); + + var session = await _client.StartSessionAsync(); + + _sessions.Add(sessionId, session); + + return sessionId; + } + + public IClientSessionHandle? GetSession(Guid id) { + _sessions.TryGetValue(id, out IClientSessionHandle? session); + return session; + } + + public void StartTransaction(Guid id) { + var session = GetSession(id); + + if (session != null) + session.StartTransaction(); + } + + public void CommitTransaction(Guid id) { + var session = GetSession(id); + + if (session != null) { + session.CommitTransaction(); + session.Dispose(); + + _sessions.Remove(id); + } + } + + public async void RollbackTransaction(Guid id) { + var session = GetSession(id); + + if (session != null) { + await session.AbortTransactionAsync(); + session.Dispose(); + + _sessions.Remove(id); + } + } + + public void DisposeSession(Guid id) { + var session = GetSession(id); + + if (session != null) { + session.Dispose(); + _sessions.Remove(id); + } + } + } +} diff --git a/webapi/DataProviders/SiteSettingsDataProvider.cs b/webapi/DataProviders/SiteSettingsDataProvider.cs deleted file mode 100644 index bf595ff..0000000 --- a/webapi/DataProviders/SiteSettingsDataProvider.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DataProviders { - public class SiteSettingsDataProvider { - } -} diff --git a/webapi/WeatherForecast/Configuration.cs b/webapi/WeatherForecast/Configuration.cs index dd026c8..9141726 100644 --- a/webapi/WeatherForecast/Configuration.cs +++ b/webapi/WeatherForecast/Configuration.cs @@ -1,5 +1,8 @@ -namespace WeatherForecast { - public class Configuration { +using DataProviders; + +namespace WeatherForecast { + public class Configuration : IDataProvidersConfig{ public string Secret { get; set; } + public Database Database { get; set; } } } diff --git a/webapi/WeatherForecast/Models/HeaderModel.cs b/webapi/WeatherForecast/Models/HeaderModel.cs new file mode 100644 index 0000000..d3e6790 --- /dev/null +++ b/webapi/WeatherForecast/Models/HeaderModel.cs @@ -0,0 +1,12 @@ +namespace WeatherForecast.Models { + + + public class HeaderModel { + public string Title { get; set; } + public Dictionary HeaderLink { get; set; } + + public Dictionary Meta { get; set; } + } + + +} diff --git a/webapi/WeatherForecast/Models/Responses/GetContentResponseModel.cs b/webapi/WeatherForecast/Models/Responses/GetContentResponseModel.cs index 093ddb3..f44d2e2 100644 --- a/webapi/WeatherForecast/Models/Responses/GetContentResponseModel.cs +++ b/webapi/WeatherForecast/Models/Responses/GetContentResponseModel.cs @@ -4,6 +4,11 @@ using WeatherForecast.Models.Pages; namespace WeatherForecast.Models.Responses { public class GetContentResponseModel : ResponseModel { public string SiteName { get; set; } + public string SiteUrl { get; set; } + + public HeaderModel Header { get; set; } + + public LocalizationModel Localization { get; set; } public List Routes { get; set; } public List AdminRoutes { get; set; } diff --git a/webapi/WeatherForecast/Services/ContentService.cs b/webapi/WeatherForecast/Services/ContentService.cs new file mode 100644 index 0000000..70ff866 --- /dev/null +++ b/webapi/WeatherForecast/Services/ContentService.cs @@ -0,0 +1,12 @@ +namespace WeatherForecast.Services { + + public interface IContentService { + + } + + public class ContentService : IContentService { + + } + + +} diff --git a/webapi/WeatherForecast/WeatherForecast.csproj b/webapi/WeatherForecast/WeatherForecast.csproj index a8a220d..280e272 100644 --- a/webapi/WeatherForecast/WeatherForecast.csproj +++ b/webapi/WeatherForecast/WeatherForecast.csproj @@ -20,6 +20,7 @@ +