Skip to content

Commit

Permalink
Merge pull request #1094 from TomaszKandula/dev
Browse files Browse the repository at this point in the history
merge: dev to master
  • Loading branch information
TomaszKandula authored May 9, 2024
2 parents 6b93aff + c1188ec commit 719ced2
Show file tree
Hide file tree
Showing 21 changed files with 232 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ namespace TokanPages.Backend.Application.Articles.Queries;

public class GetArticleQuery : IRequest<GetArticleQueryResult>
{
public Guid Id { get; set; }
public Guid? Id { get; set; }

public string? Title { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,20 @@ public override async Task<GetArticleQueryResult> Handle(GetArticleQuery request
var user = await _userService.GetUser(cancellationToken);
var isAnonymousUser = user == null;

var requestId = Guid.Empty;
if (request.Id is not null)
requestId = (Guid)request.Id;

if (!string.IsNullOrWhiteSpace(request.Title))
requestId = await GetArticleIdByTitle(request.Title, cancellationToken);

var settings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
var textAsString = await GetArticleTextContent(request.Id, cancellationToken);
var textAsString = await GetArticleTextContent(requestId, cancellationToken);
var textAsObject = _jsonSerializer.Deserialize<List<ArticleSectionDto>>(textAsString, settings);

var userLikes = await DatabaseContext.ArticleLikes
.AsNoTracking()
.Where(likes => likes.ArticleId == request.Id)
.Where(likes => likes.ArticleId == requestId)
.WhereIfElse(isAnonymousUser,
likes => likes.IpAddress == _userService.GetRequestIpAddress(),
likes => likes.UserId == user!.UserId)
Expand All @@ -50,7 +57,7 @@ public override async Task<GetArticleQueryResult> Handle(GetArticleQuery request

var totalLikes = await DatabaseContext.ArticleLikes
.AsNoTracking()
.Where(likes => likes.ArticleId == request.Id)
.Where(likes => likes.ArticleId == requestId)
.Select(likes => likes.LikeCount)
.SumAsync(cancellationToken);

Expand All @@ -59,7 +66,7 @@ join userInfo in DatabaseContext.UserInfo
on articles.UserId equals userInfo.UserId
join users in DatabaseContext.Users
on articles.UserId equals users.Id
where articles.Id == request.Id
where articles.Id == requestId
select new GetArticleQueryResult
{
Id = articles.Id,
Expand Down Expand Up @@ -93,6 +100,16 @@ on articles.UserId equals users.Id
return query;
}

private async Task<Guid> GetArticleIdByTitle(string title, CancellationToken cancellationToken = default)
{
var comparableTitle = title.Replace("-", " ").ToLower();
return await DatabaseContext.Articles
.AsNoTracking()
.Where(articles => articles.Title.ToLower() == comparableTitle)
.Select(articles => articles.Id)
.SingleOrDefaultAsync(cancellationToken);
}

private async Task<string> GetArticleTextContent(Guid articleId, CancellationToken cancellationToken)
{
var azureBlob = _azureBlobStorageFactory.Create(LoggerService);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,25 @@ namespace TokanPages.Backend.Application.Articles.Queries;

public class GetArticleQueryValidator : AbstractValidator<GetArticleQuery>
{
public GetArticleQueryValidator()
public GetArticleQueryValidator()
{
RuleFor(query => query.Id)
.NotEmpty()
.WithErrorCode(nameof(ValidationCodes.REQUIRED))
.WithMessage(ValidationCodes.REQUIRED);
When(query => query.Id != null, () =>
{
RuleFor(query => query.Id)
.NotEmpty()
.WithErrorCode(nameof(ValidationCodes.REQUIRED))
.WithMessage(ValidationCodes.REQUIRED)
.NotEqual(Guid.Empty)
.WithErrorCode(nameof(ValidationCodes.INVALID_GUID_VALUE))
.WithMessage(ValidationCodes.INVALID_GUID_VALUE);
});

When(query => query.Title != null, () =>
{
RuleFor(query => query.Title)
.NotEmpty()
.WithErrorCode(nameof(ValidationCodes.REQUIRED))
.WithMessage(ValidationCodes.REQUIRED);
});
}
}
89 changes: 45 additions & 44 deletions TokanPages.ClientApp/src/Api/Request/Paths/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const API_NON_VIDEO_ASSETS_URI = `${API_ASSETS_URI}/getNonVideoAsset`;

export const GET_ARTICLES = `${API_ARTICLES_URI}/getArticles`;
export const GET_ARTICLE = `${API_ARTICLES_URI}/{id}/getArticle`;
export const GET_ARTICLE_BY_TITLE = `${API_ARTICLES_URI}/{title}/getArticle`;
export const ADD_ARTICLE = `${API_ARTICLES_URI}/addArticle`;
export const UPDATE_ARTICLE_CONTENT = `${API_ARTICLES_URI}/updateArticleContent`;
export const UPDATE_ARTICLE_COUNT = `${API_ARTICLES_URI}/updateArticleCount`;
Expand All @@ -27,8 +28,8 @@ export const REVOKE_USER_TOKEN = `${API_USERS_URI}/revokeUserToken`;
export const REVOKE_REFRESH_TOKEN = `${API_USERS_URI}/revokeUserRefreshToken`;
export const GET_USER = `${API_USERS_URI}/{id}/getUser`;
export const GET_USERS = `${API_USERS_URI}/getUsers`;
export const GET_USER_IMAGE = `${API_USERS_URI}/{id}/getUserImage/?blobName={name}`;
export const GET_USER_VIDEO = `${API_USERS_URI}/{id}/getUserVideo/?blobName={name}`;
export const GET_USER_IMAGE = `${API_USERS_URI}/{id}/getUserImage?blobName={name}`;
export const GET_USER_VIDEO = `${API_USERS_URI}/{id}/getUserVideo?blobName={name}`;
export const ADD_USER = `${API_USERS_URI}/addUser`;
export const UPDATE_USER = `${API_USERS_URI}/updateUser`;
export const REMOVE_USER = `${API_USERS_URI}/removeUser`;
Expand All @@ -49,52 +50,52 @@ export const SEND_NEWSLETTER = `${API_MAILER_URI}/sendNewsletter`;
export const NOTIFY_WEB_URL = `${API_NOTIFICATIONS_WEB_URI}/notify`;
export const NOTIFICATION_STATUS = `${API_NOTIFICATIONS_WEB_URI}/status`;

export const GET_NON_VIDEO_ASSET = `${API_ASSETS_URI}/getNonVideoAsset/?blobName={name}`;
export const GET_VIDEO_ASSET = `${API_ASSETS_URI}/getVideoAsset/?blobName={name}`;
export const GET_NON_VIDEO_ASSET = `${API_ASSETS_URI}/getNonVideoAsset?blobName={name}`;
export const GET_VIDEO_ASSET = `${API_ASSETS_URI}/getVideoAsset?blobName={name}`;

export const GET_CONTENT_MANIFEST = `${API_CONTENT_URI}/getManifest`;
export const GET_CONTENT_TEMPLATES = `${API_CONTENT_URI}/getContent/?name=templates&type=component`;
export const GET_NAVIGATION_CONTENT = `${API_CONTENT_URI}/getContent/?name=navigation&type=component`;
export const GET_HEADER_CONTENT = `${API_CONTENT_URI}/getContent/?name=header&type=component`;
export const GET_FOOTER_CONTENT = `${API_CONTENT_URI}/getContent/?name=footer&type=component`;
export const GET_ARTICLE_FEAT_CONTENT = `${API_CONTENT_URI}/getContent/?name=articleFeatures&type=component`;
export const GET_CONTACT_FORM_CONTENT = `${API_CONTENT_URI}/getContent/?name=contactForm&type=component`;
export const GET_COOKIES_PROMPT_CONTENT = `${API_CONTENT_URI}/getContent/?name=cookiesPrompt&type=component`;
export const GET_CLIENTS_CONTENT = `${API_CONTENT_URI}/getContent/?name=clients&type=component`;
export const GET_FEATURED_CONTENT = `${API_CONTENT_URI}/getContent/?name=featured&type=component`;
export const GET_TECHNOLOGIES_CONTENT = `${API_CONTENT_URI}/getContent/?name=technologies&type=component`;
export const GET_NEWSLETTER_CONTENT = `${API_CONTENT_URI}/getContent/?name=newsletter&type=component`;
export const GET_RESET_PASSWORD_CONTENT = `${API_CONTENT_URI}/getContent/?name=resetPassword&type=component`;
export const GET_UPDATE_PASSWORD_CONTENT = `${API_CONTENT_URI}/getContent/?name=updatePassword&type=component`;
export const GET_SIGNIN_CONTENT = `${API_CONTENT_URI}/getContent/?name=userSignin&type=component`;
export const GET_SIGNUP_CONTENT = `${API_CONTENT_URI}/getContent/?name=userSignup&type=component`;
export const GET_SIGNOUT_CONTENT = `${API_CONTENT_URI}/getContent/?name=userSignout&type=component`;
export const GET_TESTIMONIALS_CONTENT = `${API_CONTENT_URI}/getContent/?name=testimonials&type=component`;
export const GET_UNSUBSCRIBE_CONTENT = `${API_CONTENT_URI}/getContent/?name=unsubscribe&type=component`;
export const GET_ACTIVATE_ACCOUNT_CONTENT = `${API_CONTENT_URI}/getContent/?name=activateAccount&type=component`;
export const GET_UPDATE_NEWSLETTER_CONTENT = `${API_CONTENT_URI}/getContent/?name=updateNewsletter&type=component`;
export const GET_WRONG_PAGE_PROMPT_CONTENT = `${API_CONTENT_URI}/getContent/?name=wrongPagePrompt&type=component`;
export const GET_ACCOUNT_CONTENT = `${API_CONTENT_URI}/getContent/?name=account&type=component`;
export const GET_ARTICLE_CONTENT = `${API_CONTENT_URI}/getContent/?name=article&type=component`;
export const GET_STORY_CONTENT = `${API_CONTENT_URI}/getContent/?name=story&type=document`;
export const GET_TERMS_CONTENT = `${API_CONTENT_URI}/getContent/?name=terms&type=document`;
export const GET_POLICY_CONTENT = `${API_CONTENT_URI}/getContent/?name=policy&type=document`;
export const GET_SHOWCASE_CONTENT = `${API_CONTENT_URI}/getContent/?name=showcase&type=document`;
export const GET_BICYCLE_CONTENT = `${API_CONTENT_URI}/getContent/?name=bicycle&type=document`;
export const GET_ELECTRONICS_CONTENT = `${API_CONTENT_URI}/getContent/?name=electronics&type=document`;
export const GET_FOOTBALL_CONTENT = `${API_CONTENT_URI}/getContent/?name=football&type=document`;
export const GET_GUITAR_CONTENT = `${API_CONTENT_URI}/getContent/?name=guitar&type=document`;
export const GET_PHOTOGRAPHY_CONTENT = `${API_CONTENT_URI}/getContent/?name=photography&type=document`;
export const GET_CONTENT_TEMPLATES = `${API_CONTENT_URI}/getContent?name=templates&type=component`;
export const GET_NAVIGATION_CONTENT = `${API_CONTENT_URI}/getContent?name=navigation&type=component`;
export const GET_HEADER_CONTENT = `${API_CONTENT_URI}/getContent?name=header&type=component`;
export const GET_FOOTER_CONTENT = `${API_CONTENT_URI}/getContent?name=footer&type=component`;
export const GET_ARTICLE_FEAT_CONTENT = `${API_CONTENT_URI}/getContent?name=articleFeatures&type=component`;
export const GET_CONTACT_FORM_CONTENT = `${API_CONTENT_URI}/getContent?name=contactForm&type=component`;
export const GET_COOKIES_PROMPT_CONTENT = `${API_CONTENT_URI}/getContent?name=cookiesPrompt&type=component`;
export const GET_CLIENTS_CONTENT = `${API_CONTENT_URI}/getContent?name=clients&type=component`;
export const GET_FEATURED_CONTENT = `${API_CONTENT_URI}/getContent?name=featured&type=component`;
export const GET_TECHNOLOGIES_CONTENT = `${API_CONTENT_URI}/getContent?name=technologies&type=component`;
export const GET_NEWSLETTER_CONTENT = `${API_CONTENT_URI}/getContent?name=newsletter&type=component`;
export const GET_RESET_PASSWORD_CONTENT = `${API_CONTENT_URI}/getContent?name=resetPassword&type=component`;
export const GET_UPDATE_PASSWORD_CONTENT = `${API_CONTENT_URI}/getContent?name=updatePassword&type=component`;
export const GET_SIGNIN_CONTENT = `${API_CONTENT_URI}/getContent?name=userSignin&type=component`;
export const GET_SIGNUP_CONTENT = `${API_CONTENT_URI}/getContent?name=userSignup&type=component`;
export const GET_SIGNOUT_CONTENT = `${API_CONTENT_URI}/getContent?name=userSignout&type=component`;
export const GET_TESTIMONIALS_CONTENT = `${API_CONTENT_URI}/getContent?name=testimonials&type=component`;
export const GET_UNSUBSCRIBE_CONTENT = `${API_CONTENT_URI}/getContent?name=unsubscribe&type=component`;
export const GET_ACTIVATE_ACCOUNT_CONTENT = `${API_CONTENT_URI}/getContent?name=activateAccount&type=component`;
export const GET_UPDATE_NEWSLETTER_CONTENT = `${API_CONTENT_URI}/getContent?name=updateNewsletter&type=component`;
export const GET_WRONG_PAGE_PROMPT_CONTENT = `${API_CONTENT_URI}/getContent?name=wrongPagePrompt&type=component`;
export const GET_ACCOUNT_CONTENT = `${API_CONTENT_URI}/getContent?name=account&type=component`;
export const GET_ARTICLE_CONTENT = `${API_CONTENT_URI}/getContent?name=article&type=component`;
export const GET_STORY_CONTENT = `${API_CONTENT_URI}/getContent?name=story&type=document`;
export const GET_TERMS_CONTENT = `${API_CONTENT_URI}/getContent?name=terms&type=document`;
export const GET_POLICY_CONTENT = `${API_CONTENT_URI}/getContent?name=policy&type=document`;
export const GET_SHOWCASE_CONTENT = `${API_CONTENT_URI}/getContent?name=showcase&type=document`;
export const GET_BICYCLE_CONTENT = `${API_CONTENT_URI}/getContent?name=bicycle&type=document`;
export const GET_ELECTRONICS_CONTENT = `${API_CONTENT_URI}/getContent?name=electronics&type=document`;
export const GET_FOOTBALL_CONTENT = `${API_CONTENT_URI}/getContent?name=football&type=document`;
export const GET_GUITAR_CONTENT = `${API_CONTENT_URI}/getContent?name=guitar&type=document`;
export const GET_PHOTOGRAPHY_CONTENT = `${API_CONTENT_URI}/getContent?name=photography&type=document`;

export const GET_ARTICLE_MAIN_IMAGE_URL = `${API_ASSETS_URI}/getArticleAsset/?id={id}&assetName=image.jpg`;
export const GET_ARTICLE_MAIN_IMAGE_URL = `${API_ASSETS_URI}/getArticleAsset?id={id}&assetName=image.jpg`;

export const GET_IMAGES_URL = `${API_NON_VIDEO_ASSETS_URI}/?blobName=images`;
export const GET_ARTICLE_IMAGE_URL = `${API_NON_VIDEO_ASSETS_URI}/?blobName=images/sections/articles`;
export const GET_FEATURED_IMAGE_URL = `${API_NON_VIDEO_ASSETS_URI}/?blobName=images/sections/featured`;
export const GET_TESTIMONIALS_URL = `${API_NON_VIDEO_ASSETS_URI}/?blobName=images/sections/testimonials`;
export const GET_ICONS_URL = `${API_NON_VIDEO_ASSETS_URI}/?blobName=images/icons`;
export const GET_FLAG_URL = `${API_NON_VIDEO_ASSETS_URI}/?blobName=images/flags`;
export const GET_IMAGES_URL = `${API_NON_VIDEO_ASSETS_URI}?blobName=images`;
export const GET_ARTICLE_IMAGE_URL = `${API_NON_VIDEO_ASSETS_URI}?blobName=images/sections/articles`;
export const GET_FEATURED_IMAGE_URL = `${API_NON_VIDEO_ASSETS_URI}?blobName=images/sections/featured`;
export const GET_TESTIMONIALS_URL = `${API_NON_VIDEO_ASSETS_URI}?blobName=images/sections/testimonials`;
export const GET_ICONS_URL = `${API_NON_VIDEO_ASSETS_URI}?blobName=images/icons`;
export const GET_FLAG_URL = `${API_NON_VIDEO_ASSETS_URI}?blobName=images/flags`;

export const MAIN_ICON = `${GET_ICONS_URL}/main_logo.svg`;
export const MEDIUM_ICON = `${GET_ICONS_URL}/medium_icon.svg`;
export const ARTICLE_PATH = "/articles/?id={id}";
export const ARTICLE_PATH = "/articles?title={title}";
1 change: 1 addition & 0 deletions TokanPages.ClientApp/src/Api/Request/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export {
API_BASE_URI,
GET_ARTICLES,
GET_ARTICLE,
GET_ARTICLE_BY_TITLE,
ADD_ARTICLE,
UPDATE_ARTICLE_CONTENT,
UPDATE_ARTICLE_COUNT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ exports[`test articles group component: ArticleCardView should render correctly
<img
alt=""
className="makeStyles-flag_image-7"
src="http://localhost:5000/api/v1/content/assets/getNonVideoAsset/?blobName=images/flags/eng.png"
src="http://localhost:5000/api/v1/content/assets/getNonVideoAsset?blobName=images/flags/eng.png"
/>
</WithStyles(ForwardRef(CardMedia))>
<WithStyles(ForwardRef(CardContent))>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ interface Properties {

export const ArticleCard = (props: Properties): JSX.Element => {
const content = useSelector((state: ApplicationState) => state.contentArticle);
const articleUrl = ARTICLE_PATH.replace("{id}", props.id);
const quaryableTitle = props.title.replaceAll(" ", "-").toLowerCase();
const articleUrl = ARTICLE_PATH.replace("{title}", quaryableTitle);
const imageUrl = GET_ARTICLE_MAIN_IMAGE_URL.replace("{id}", props.id);

const dispatch = useDispatch();
const history = useHistory();

const onClickEvent = React.useCallback(() => {
dispatch(ArticleSelectionAction.select(props.id));
dispatch(ArticleSelectionAction.select({ id: props.id }));
history.push(articleUrl);
}, [props.id, articleUrl]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ exports[`test articles group component: ArticleDetailView should render correctl
<img
alt=""
className="makeStyles-flag_image-8"
src="http://localhost:5000/api/v1/content/assets/getNonVideoAsset/?blobName=images/flags/eng.png"
src="http://localhost:5000/api/v1/content/assets/getNonVideoAsset?blobName=images/flags/eng.png"
/>
</div>
<WithStyles(ForwardRef(Typography))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { ArticleDetailView } from "./View/articleDetailView";
import Validate from "validate.js";

interface Properties {
id: string;
title: string;
}

export const ArticleDetail = (props: Properties): JSX.Element => {
Expand All @@ -26,7 +26,7 @@ export const ArticleDetail = (props: Properties): JSX.Element => {
const user = useSelector((state: ApplicationState) => state.userDataStore);

if (Validate.isEmpty(selection.article.id) && !selection.isLoading) {
dispatch(ArticleSelectionAction.select(props.id));
dispatch(ArticleSelectionAction.select({ title: props.title }));
}

const [popoverElement, setPopover] = React.useState<HTMLElement | null>(null);
Expand Down Expand Up @@ -77,20 +77,21 @@ export const ArticleDetail = (props: Properties): JSX.Element => {

React.useEffect(() => {
if (selection.isLoading) return;
if (Validate.isEmpty(selection.article.id)) return;
dispatch(
ArticleUpdateAction.updateCount({
id: props.id,
id: selection.article.id,
})
);
}, [props.id, selection.isLoading]);
}, [selection.article.id, selection.isLoading]);

React.useEffect(() => {
const intervalId = setInterval(() => {
if (userLikes === 0 || !isThumbClicked) return;

dispatch(
ArticleUpdateAction.updateLikes({
id: props.id,
id: selection.article.id,
addToLikes: userLikes,
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ exports[`test articles group component: ArticleFeatureView should render correct
>
<div
class="MuiCardMedia-root makeStyles-media-8"
style="background-image:url(\\"http://localhost:5000/api/v1/content/assets/getNonVideoAsset/?blobName=images/sections/articles/image1.jpg\\")"
style="background-image:url(\\"http://localhost:5000/api/v1/content/assets/getNonVideoAsset?blobName=images/sections/articles/image1.jpg\\")"
/>
</div>
</div>
Expand All @@ -122,7 +122,7 @@ exports[`test articles group component: ArticleFeatureView should render correct
>
<div
class="MuiCardMedia-root makeStyles-media-8"
style="background-image:url(\\"http://localhost:5000/api/v1/content/assets/getNonVideoAsset/?blobName=images/sections/articles/image2.jpg\\")"
style="background-image:url(\\"http://localhost:5000/api/v1/content/assets/getNonVideoAsset?blobName=images/sections/articles/image2.jpg\\")"
/>
</div>
</div>
Expand All @@ -134,7 +134,7 @@ exports[`test articles group component: ArticleFeatureView should render correct
>
<div
class="MuiCardMedia-root makeStyles-media-8"
style="background-image:url(\\"http://localhost:5000/api/v1/content/assets/getNonVideoAsset/?blobName=images/sections/articles/image3.jpg\\")"
style="background-image:url(\\"http://localhost:5000/api/v1/content/assets/getNonVideoAsset?blobName=images/sections/articles/image3.jpg\\")"
/>
</div>
</div>
Expand All @@ -146,7 +146,7 @@ exports[`test articles group component: ArticleFeatureView should render correct
>
<div
class="MuiCardMedia-root makeStyles-media-8"
style="background-image:url(\\"http://localhost:5000/api/v1/content/assets/getNonVideoAsset/?blobName=images/sections/articles/image4.jpg\\")"
style="background-image:url(\\"http://localhost:5000/api/v1/content/assets/getNonVideoAsset?blobName=images/sections/articles/image4.jpg\\")"
/>
</div>
</div>
Expand Down
Loading

0 comments on commit 719ced2

Please sign in to comment.