Skip to content

Commit

Permalink
Merge pull request #2 from soasada/blog_post_update
Browse files Browse the repository at this point in the history
Blog post update
  • Loading branch information
soasada authored Jan 24, 2021
2 parents e496281 + 22741fa commit 00ec1ac
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.kemenu.admin.application.blog
import com.kemenu.admin.domain.BlogPost
import com.kemenu.admin.domain.Post
import org.bson.types.ObjectId
import java.time.Instant

object BlogPostMapper {

Expand All @@ -11,6 +12,6 @@ object BlogPostMapper {
}

fun toUpdateEntity(old: BlogPost, request: BlogPostRequest): BlogPost {
return BlogPost(old.id, request.imageUrl, old.posts, old.createdAt)
return BlogPost(old.id, request.imageUrl, old.posts, old.createdAt, Instant.now())
}
}
2 changes: 1 addition & 1 deletion kemenu-admin-frontend/src/blog/BlogPostCreate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import BorderBottomTitle from '@/layout/BorderBottomTitle.vue';
import {useRoute, useRouter} from 'vue-router';
export default defineComponent({
name: 'BlogCreate',
name: 'BlogPostCreate',
components: {
BorderBottomTitle
},
Expand Down
68 changes: 68 additions & 0 deletions kemenu-admin-frontend/src/blog/BlogPostUpdate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<template>
<div class="blog-post-create">
<BorderBottomTitle title="Update a Blog post"/>

<form @submit.prevent="sendForm">
<div class="mb-3">
<label for="input-blog-title" class="form-label">Title</label>
<input type="text" class="form-control" id="input-blog-title" v-model="blogPost.title">
</div>

<div class="mb-3">
<label for="input-blog-content" class="form-label">Content</label>
<textarea class="form-control" id="input-blog-content" rows="3" v-model="blogPost.content"></textarea>
</div>

<div class="mb-3">
<label for="input-blog-locale" class="form-label">Locale</label>
<select id="input-blog-locale" class="form-select" v-model="blogPost.locale">
<option value="es">Spanish</option>
<option value="en">English</option>
<option value="ca">Catalan</option>
</select>
</div>

<button type="submit" class="btn btn-primary">Update</button>
</form>
</div>
</template>

<script lang="ts">
import {defineComponent} from 'vue';
import {useStore} from 'vuex';
import BorderBottomTitle from '@/layout/BorderBottomTitle.vue';
import {useRoute, useRouter} from 'vue-router';
import BlogService from '@/blog/BlogService';
export default defineComponent({
name: 'BlogPostUpdate',
components: {
BorderBottomTitle
},
setup() {
const store = useStore();
const route = useRoute();
const router = useRouter();
const blogId = route.params.blogId as string;
const locale = route.params.locale as string;
const findBlog = store.getters.findBlog;
const blog = findBlog(blogId);
const blogPost = blog.posts[locale];
const sendForm = () => {
const token = store.getters.getAccessToken;
store.dispatch('clearBlogs'); // this should be inside the service
BlogService.update(blogId, {
title: blogPost.title,
content: blogPost.content,
imageUrl: blog.imageUrl,
locale: blogPost.locale
}, token, () => router.push('/blog'));
};
return {store, route, router, blogPost, sendForm};
}
});
</script>
9 changes: 3 additions & 6 deletions kemenu-admin-frontend/src/blog/BlogService.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import BlogResponse from '@/blog/BlogResponse';
import {Router} from 'vue-router';
import CRUDService from '@/crud/CRUDService';
import BlogRequest from '@/blog/BlogRequest';
import {UnwrapRef} from 'vue';

export default class BlogService {
private static ENDPOINT = '/v1/blog';
Expand All @@ -23,9 +20,9 @@ export default class BlogService {
.catch(e => console.error(e));
}

static update(newBlog: BlogResponse, token: string, router: UnwrapRef<Router>): void {
CRUDService.update(BlogService.ENDPOINT + '/' + newBlog.id, newBlog, token)
.then(() => router.push('/blog'))
static update(blogId: string, newBlog: BlogRequest, token: string, onSuccess: () => void): void {
CRUDService.update(blogId, BlogService.ENDPOINT, newBlog, token)
.then(onSuccess)
.catch(e => console.error(e));
}
}
4 changes: 2 additions & 2 deletions kemenu-admin-frontend/src/blog/BlogUpdate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<ul class="list-group">
<li class="list-group-item" v-for="post in posts" :key="post.id">
<PostItem :post="post"/>
<PostItem :post="post" :blog-id="blogId"/>
</li>
</ul>
</div>
Expand Down Expand Up @@ -39,7 +39,7 @@ export default defineComponent({
router.push({path: '/blog/' + blogId + '/post', query: {imageUrl: blog.imageUrl}});
};
return {posts, createBlogPost};
return {blogId, posts, createBlogPost};
}
});
</script>
6 changes: 5 additions & 1 deletion kemenu-admin-frontend/src/blog/PostItem.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="row post-item">
<div class="col-3">
<router-link class="nav-link" :to="'/blog/post/update/' + post.readableId">
<router-link class="nav-link" :to="'/blog/' + blogId + '/locale/' + post.locale + '/update'">
{{ post.title }}
</router-link>
</div>
Expand All @@ -24,6 +24,10 @@ import PostResponse from '@/blog/PostResponse';
export default defineComponent({
name: 'PostItem',
props: {
blogId: {
type: String,
required: true
},
post: {
type: Object as PropType<PostResponse>,
required: true
Expand Down
31 changes: 9 additions & 22 deletions kemenu-admin-frontend/src/blog/PostResponse.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,10 @@
export default class PostResponse {
readonly id: string;
readonly readableId: string;
readonly title: string;
readonly content: string;
readonly locale: string;
readonly createdBy: string;
readonly updatedBy: string;
readonly createdAt: string;
readonly updatedAt: string;

constructor(readableId: string, title: string, content: string, locale: string, createdBy: string, updatedBy: string, createdAt: string, updatedAt: string) {
this.id = readableId;
this.readableId = readableId;
this.title = title;
this.content = content;
this.locale = locale;
this.createdBy = createdBy;
this.updatedBy = updatedBy;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
export default interface PostResponse {
readableId: string;
title: string;
content: string;
locale: string;
createdBy: string;
updatedBy: string;
createdAt: string;
updatedAt: string;
}
6 changes: 6 additions & 0 deletions kemenu-admin-frontend/src/blog/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ const blogRoutes: Array<RouteRecordRaw> = [
name: 'BlogPostCreate',
component: () => import(/* webpackChunkName: "blogPostCreate" */ './BlogPostCreate.vue'),
meta: {requiresAuth: true}
},
{
path: '/blog/:blogId/locale/:locale/update',
name: 'BlogPostUpdate',
component: () => import(/* webpackChunkName: "blogPostUpdate" */ './BlogPostUpdate.vue'),
meta: {requiresAuth: true}
}
];

Expand Down
8 changes: 2 additions & 6 deletions kemenu-admin-frontend/src/crud/CRUDService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,9 @@ export default class CRUDService {
}
}

static async update<T extends Identifiable>(endpoint: string, newEntity: T, token: string): Promise<Response> {
if (!newEntity.id) {
throw new Error('Entity should have id');
}

static async update<T extends object>(id: string, endpoint: string, newEntity: T, token: string): Promise<Response> {
try {
return await HttpClient.put(endpoint + '/' + newEntity.id, newEntity, token);
return await HttpClient.put(endpoint + '/' + id, newEntity, token);
} catch (e) {
console.error(e);
throw new Error(e);
Expand Down
20 changes: 10 additions & 10 deletions kemenu-admin-frontend/tests/unit/blog/PostResponseHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ export function randomReadableId(): string {
}

export function randomPost(): PostResponse {
return new PostResponse(
randomReadableId(),
chance.sentence(),
chance.paragraph(),
chance.locale(),
chance.email(),
chance.email(),
chance.date().toISOString(),
chance.date().toISOString()
);
return {
readableId: randomReadableId(),
title: chance.sentence(),
content: chance.paragraph(),
locale: chance.locale(),
createdBy: chance.email(),
updatedBy: chance.email(),
createdAt: chance.date().toISOString(),
updatedAt: chance.date().toISOString()
};
}
36 changes: 36 additions & 0 deletions kemenu-admin-frontend/tests/unit/upload_image/UploadImage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {mount} from '@vue/test-utils';
import UploadImage from '@/upload_image/UploadImage.vue';
import store from '@/store';
import UploadImageService from '@/upload_image/UploadImageService';
import {ref} from 'vue';

describe('UploadImage.vue', () => {
it('Should emit the URL when an image is uploaded', async () => {
const mockUpload = jest.fn();
const expectedURL = 'https://example.com';
mockUpload.mockReturnValueOnce(
new Promise(
(resolve) => {
resolve(expectedURL);
}
)
);
UploadImageService.upload = mockUpload;
const imageUrl = ref('');
const wrapper = mount(UploadImage, {
props: {
modelValue: imageUrl
},
global: {
plugins: [store]
}
});
const input = wrapper.find('input');
(wrapper.vm as any).file = {
files: [new File([''], 'example.png', {type: 'image/png'})]
};
await input.trigger('change');
let emittedElement = wrapper.emitted('update:modelValue')[0] as String[];
expect(emittedElement[0]).toStrictEqual(expectedURL);
});
});
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<version>2.4.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>

Expand Down

0 comments on commit 00ec1ac

Please sign in to comment.