Skip to content

Commit c3949a4

Browse files
committed
feature - make sharable links for owned bookmarks
1 parent 93eabfe commit c3949a4

24 files changed

+195
-27
lines changed

backend/package-lock.json

Lines changed: 17 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"showdown": "^1.9.1",
3232
"superagent": "^4.1.0",
3333
"swagger-ui-express": "^4.3.0",
34+
"uuid": "^9.0.0",
3435
"yamljs": "^0.3.0"
3536
},
3637
"devDependencies": {

backend/src/model/bookmark.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const mongoose = require('mongoose');
22
const Schema = mongoose.Schema;
33

44
const bookmarkSchema = new Schema({
5+
shareableId: {type:String, select: false},
56
name: {type:String, required: true},
67
location: {type:String, required: true},
78
description: String,

backend/src/routes/public/public-bookmarks.router.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ router.get('/', async (request, response, next) => {
1616
const sort = request.query.sort;
1717
const {page, limit} = PaginationQueryParamsHelper.getPageAndLimit(request);
1818

19-
if (searchText) {
19+
if ( searchText ) {
2020
const bookmarks = await publicBookmarksSearchService.findPublicBookmarks(searchText, page, limit, sort, searcMode);
2121
response.send(bookmarks);
2222
} else {
@@ -29,7 +29,7 @@ router.get('/', async (request, response, next) => {
2929
*/
3030
router.get('/', async (request, response, next) => {
3131
const location = request.query.location;
32-
if (location) {
32+
if ( location ) {
3333
const bookmarksForLocation = await PublicBookmarksService.getPublicBookmarkByLocation(location);
3434

3535
return response.send(bookmarksForLocation);
@@ -38,11 +38,20 @@ router.get('/', async (request, response, next) => {
3838
}
3939
});
4040

41+
/**
42+
* Get Bookmark by shareableId
43+
*/
44+
router.get('/shared/:shareableId', async (request, response, next) => {
45+
const shareableId = request.params.shareableId;
46+
const sharedBookmark = await PublicBookmarksService.getBookmarkBySharableId(shareableId);
47+
48+
return response.json(sharedBookmark);
49+
});
50+
4151
/**
4252
* When no filter send latest public bookmarks
4353
*/
4454
router.get('/', async (request, response) => {
45-
4655
const {page, limit} = PaginationQueryParamsHelper.getPageAndLimit(request);
4756
const bookmarks = await PublicBookmarksService.getLatestPublicBookmarks(page, limit);
4857

@@ -71,7 +80,7 @@ router.get('/tagged/:tag', async (request, response) => {
7180
/* GET title of bookmark given its url - might be moved to front-end */
7281
router.get('/scrape', async function (request, response, next) {
7382
const location = request.query.location;
74-
if (location) {
83+
if ( location ) {
7584
const webpageData = await PublicBookmarksService.getScrapedDataForLocation(location);
7685

7786
return response.send(webpageData);

backend/src/routes/public/public-bookmarks.service.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,20 @@ let getBookmarkById = async function (bookmarkId) {
6262
return bookmark;
6363
};
6464

65-
let getMostUsedPublicTags = async function (limit) {
65+
/* GET bookmark of user by shareableId */
66+
let getBookmarkBySharableId = async (shareableId) => {
67+
const bookmark = await Bookmark.findOne({
68+
shareableId: shareableId
69+
}).select('+shareableId');
70+
71+
if ( !bookmark ) {
72+
throw new NotFoundError(`Bookmark NOT_FOUND for shareableId: ${shareableId}`);
73+
} else {
74+
return bookmark;
75+
}
76+
};
6677

78+
let getMostUsedPublicTags = async function (limit) {
6779
const aggregatedTags = await Bookmark.aggregate([
6880
//first stage - filter
6981
{
@@ -112,5 +124,6 @@ module.exports = {
112124
getLatestPublicBookmarks: getLatestPublicBookmarks,
113125
getBookmarksForTag: getBookmarksForTag,
114126
getBookmarkById: getBookmarkById,
127+
getBookmarkBySharableId: getBookmarkBySharableId,
115128
getMostUsedPublicTags: getMostUsedPublicTags
116129
};

backend/src/routes/users/bookmarks/personal-bookmarks.router.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,16 @@ personalBookmarksRouter.get('/:bookmarkId', keycloak.protect(), async (request,
115115
return response.status(HttpStatus.OK).send(bookmark);
116116
});
117117

118+
/* GET sharableId for bookmark */
119+
personalBookmarksRouter.get('/shareable/:bookmarkId', keycloak.protect(), async (request, response) => {
120+
UserIdValidator.validateUserId(request);
121+
122+
const {userId, bookmarkId} = request.params;
123+
const shareableId = await PersonalBookmarksService.getOrCreateSharableId(userId, bookmarkId);
124+
const sharableIdResponse = {"shareableId": shareableId}
125+
return response.json(sharableIdResponse);
126+
});
127+
118128
/**
119129
* full UPDATE via PUT - that is the whole document is required and will be updated
120130
* the descriptionHtml parameter is only set in backend, if only does not come front-end (might be an API call)

backend/src/routes/users/bookmarks/personal-bookmarks.service.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ const User = require('../../../model/user');
44
const NotFoundError = require('../../../error/not-found.error');
55

66
const BookmarkInputValidator = require('../../../common/bookmark-input.validator');
7+
const {
8+
v4: uuidv4,
9+
} = require('uuid');
710

811
/**
912
* CREATE bookmark for user
@@ -191,6 +194,32 @@ let increaseOwnerVisitCount = async (userId, bookmarkId) => {
191194
);
192195
}
193196

197+
let getOrCreateShareableId = async (userId, bookmarkId) => {
198+
const bookmark = await Bookmark.findOne(
199+
{
200+
_id: bookmarkId,
201+
userId: userId
202+
}).select('+shareableId');
203+
204+
if ( bookmark.shareableId ) {
205+
return bookmark.shareableId
206+
} else {
207+
const uuid = uuidv4();
208+
const updatedBookmark = await Bookmark.findOneAndUpdate(
209+
{
210+
_id: bookmarkId,
211+
userId: userId
212+
},
213+
{
214+
$set: {shareableId: uuid}
215+
},
216+
{new: true}
217+
).select('+shareableId');
218+
219+
return updatedBookmark.shareableId;
220+
}
221+
}
222+
194223

195224
/*
196225
* DELETE bookmark for user
@@ -295,5 +324,6 @@ module.exports = {
295324
deleteBookmarkByLocation: deleteBookmarkByLocation,
296325
deletePrivateBookmarksByTag: deletePrivateBookmarksByTag,
297326
getUserTagsAggregated: getUserTagsAggregated,
298-
updateDisplayNameInBookmarks: updateDisplayNameInBookmarks
327+
updateDisplayNameInBookmarks: updateDisplayNameInBookmarks,
328+
getOrCreateSharableId: getOrCreateShareableId
299329
};

frontend/src/app/core/auth/auth-guard.service.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Injectable } from '@angular/core';
22
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
33
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
4-
import { environment } from '../../../environments/environment';
54

65
@Injectable()
76
export class AuthGuard extends KeycloakAuthGuard {

frontend/src/app/core/model/bookmark.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export interface Bookmark {
22
_id?: string;
3+
shareableId?: string;
34
name: string;
45
location: string;
56
description?: string;

frontend/src/app/core/personal-bookmarks.service.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ export class PersonalBookmarksService {
9393
.pipe(shareReplay(1));
9494
}
9595

96+
createOrGetShareableId(userId: string, bookmarkId: string): Observable<any> {
97+
return this.httpClient
98+
.get<string>(`${this.personalBookmarksApiBaseUrl}/${userId}/bookmarks/shareable/${bookmarkId}`)
99+
.pipe(shareReplay(1));
100+
}
101+
96102
increaseOwnerVisitCount(bookmark: Bookmark) {
97103
return this.httpClient
98104
.post(`${this.personalBookmarksApiBaseUrl}/${bookmark.userId}/bookmarks/${bookmark._id}/owner-visits/inc`, {},

0 commit comments

Comments
 (0)