-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathshortener-service.js
110 lines (96 loc) · 2.73 KB
/
shortener-service.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
* @fileoverview Methods for shortening and looking up URLs.
*/
'use strict'
const immutable = require('immutable')
const responses = require('./responses')
const utils = require('./utils')
const UrlRecord = immutable.Record({
targetUrl: '',
shortPath: '',
createdAt: 0,
isCustomPath: false,
})
class ShortenerService {
constructor(urlTable) {
this._urlTable = urlTable
}
/**
* @param {customPath} string
* @param {url} string
* @return {!Promise.<!UrlRecord>}
*/
createCustomShortUrl(customPath, url) {
return this.getUrlRecordByPath(customPath).then((urlRecord) => {
if (urlRecord) {
throw new responses.ApiError(409, `Custom path "${customPath}" is already taken`)
}
return this.putUrlRecord(customPath, url, /* isCustomPath */ true)
})
}
/**
* @param {url} string
* @return {!Promise.<!UrlRecord>}
*/
getOrCreateRandomShortUrl(url) {
return this.getUrlRecordByTargetUrl(url).then((existingRecord) => {
if (existingRecord) return existingRecord
return this.createRandomShortUrl(url)
})
}
/**
* @param {url} string
* @return {!Promise.<!UrlRecord>}
*/
createRandomShortUrl(url) {
const path = utils.generateRandomPath()
return this.getUrlRecordByPath(path).then((urlRecord) => {
// In the rare case we generate an existing random URL, try again
if (urlRecord) return this.createRandomShortUrl(url)
return this.putUrlRecord(path, url)
})
}
/**
* @param {string} path
* @param {string} url
* @param {boolean=} opt_isCustomPath
* @return {!Promise.<!UrlRecord>}
*/
putUrlRecord(path, url, opt_isCustomPath) {
const record = new UrlRecord({
targetUrl: url,
shortPath: path,
createdAt: Date.now(),
isCustomPath: !!opt_isCustomPath,
})
return this._urlTable.set(path, record.toJS()).then(() => record)
}
/**
* @param {string} shortPath
* @return {!Promise.<UrlRecord>}
*/
getUrlRecordByPath(shortPath) {
return this._urlTable.get(shortPath)
.then((snapshot) => snapshot.val() && new UrlRecord(snapshot.val()))
}
/**
* @param {string} targetUrl
* @return {!Promise.<UrlRecord>}
*/
getUrlRecordByTargetUrl(targetUrl) {
return this._urlTable.queryChildEqualTo('targetUrl', targetUrl)
.then((snapshot) => {
const records = []
snapshot.forEach((data) => {
const record = data.val()
if (!record.isCustomPath) records.push(record)
})
if (records.length > 1) {
console.error(
`Found multiple paths for ${targetUrl}: ${records.length}`)
}
return records[0] ? new UrlRecord(records[0]) : null
})
}
}
module.exports = ShortenerService