-
Notifications
You must be signed in to change notification settings - Fork 4
/
IrreversibleURLShortener.cfc
142 lines (117 loc) · 6.6 KB
/
IrreversibleURLShortener.cfc
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<cfcomponent displayname="IrreversibleURLShortener" hint="I provide functions for generating an irreversible key of variable length to be used as a shortened URL variable.">
<!--- Pseudo-constructor --->
<cfset variables.instance = {
datasource = ''
} />
<cffunction name="init" access="public" output="false" returntype="any" hint="I am the constructor method of the IrreversibleURLShortener class.">
<cfargument name="datasource" type="any" required="true" hint="I am the Datasource model/bean, with functions getDSN(), getUsername() and getPassword()." />
<!--- Set the initial values of the instance --->
<cfset variables.instance.datasource = ARGUMENTS.datasource />
<cfreturn this>
</cffunction>
<!--- --->
<!--- GET SHORT URL --->
<!--- --->
<cffunction name="getShortURL" access="public" returntype="string" output="false" hint="I return a random, verified short URL.">
<cfargument name="table" type="string" required="true" hint="I am the name of the table in the database where [column] is stored." />
<cfargument name="column" type="string" required="true" hint="I am the name of the column in [table] where the generated key is compared." />
<cfargument name="keyLength" type="numeric" required="false" default="6" hint="I am the length of the random key you wish to return." />
<cfargument name="method" type="string" required="false" default="hash" hint="I am the method of key generation, one of: hash, alpha, alphanum, or numeric." />
<!--- var scope and get the first key from the getKey() function --->
<cfset var thisKey = getKey(
keyLength = ARGUMENTS.keyLength,
method = ARGUMENTS.method
) />
<!--- var scope and check the first key using the checkKey() function --->
<cfset var isValidKey = checkKey(
checkKey = thisKey,
table = ARGUMENTS.table,
column = ARGUMENTS.column
) />
<!--- loop while there is already a matching key already used in [table].[column] --->
<cfloop condition="NOT isValidKey">
<!--- get the next iteration of a generated key to check --->
<cfset var thisKey = getKey(
keyLength = ARGUMENTS.keyLength,
method = ARGUMENTS.method
) />
<!--- check if this key is valid (not matching one already used in [table].[column]) --->
<cfset var isValidKey = checkKey(
checkKey = thisKey,
table = ARGUMENTS.table,
column = ARGUMENTS.column
) />
</cfloop>
<!--- return the generated, validated, random key --->
<cfreturn thisKey />
</cffunction>
<!--- --->
<!--- GET KEY --->
<!--- --->
<cffunction name="getKey" access="private" returntype="string" output="false" hint="I return a random key to use for the URL.">
<cfargument name="keyLength" type="numeric" required="true" hint="I am the length of the random key you wish to return." />
<cfargument name="method" type="string" required="true" hint="I am the method of key generation, one of: hash, alpha, alphanum, or numeric." />
<!--- var scope set up a list of alpha-numeric characters to use for generating an alphanum key --->
<cfset var alphaOnly = 'A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z' />
<cfset var numOnly = '0,1,2,3,4,5,6,7,8,9' />
<cfset var alphaNum = ListAppend(alphaOnly,numOnly) />
<cfset var thisKey = '' />
<cfset var keySeed = '' />
<cfset var iX = 0 />
<!--- check if we're using the 'hash' or 'alphanum' method of key generation --->
<cfif FindNoCase('hash',ARGUMENTS.method)>
<!--- using the 'hash' method, generate a hashed variable to work with --->
<cfset keySeed = Hash(CreateUUID(),'SHA-384') />
<!--- get a random string of [keyLength] characters from the generated hash --->
<cfset thisKey = Mid(keySeed,RandRange(1,Len(keySeed)-(ARGUMENTS.keyLength+1)),ARGUMENTS.keyLength) />
<!--- otherwise, check if we're using the 'alphanum' method of key generation --->
<cfelseif FindNoCase('alphanum',ARGUMENTS.method)>
<!--- using the 'alphanum' method, loop through [keyLength] times --->
<cfloop from="1" to="#ARGUMENTS.keyLength#" index="iX">
<!--- and add a random char from the [alphaNum] list --->
<cfset thisKey = thisKey & ListGetAt(alphaNum,RandRange(1,ListLen(alphaNum))) />
</cfloop>
<!--- otherwise, check if we're using the 'alpha' method of key generation --->
<cfelseif FindNoCase('alpha',ARGUMENTS.method)>
<!--- using the 'alpha' method, loop through [keyLength] times --->
<cfloop from="1" to="#ARGUMENTS.keyLength#" index="iX">
<!--- and add a random char from the [alphaOnly] list --->
<cfset thisKey = thisKey & ListGetAt(alphaOnly,RandRange(1,ListLen(alphaOnly))) />
</cfloop>
<!--- otherwise, check if we're using the 'numeric' method of key generation --->
<cfelseif FindNoCase('numeric',ARGUMENTS.method)>
<!--- using the 'numeric' method, loop through [keyLength] times --->
<cfloop from="1" to="#ARGUMENTS.keyLength#" index="iX">
<!--- and add a random char from the [numOnly] list --->
<cfset thisKey = thisKey & ListGetAt(numOnly,RandRange(1,ListLen(numOnly))) />
</cfloop>
</cfif>
<!--- return the generated key --->
<cfreturn thisKey />
</cffunction>
<!--- --->
<!--- CHECK KEY --->
<!--- --->
<cffunction name="checkKey" access="private" returntype="boolean" output="false" hint="I check a passed key and return true or false.">
<cfargument name="checkKey" type="string" required="true" hint="I am the key to check for the existence of." />
<cfargument name="table" type="string" required="true" hint="I am the name of the table in the database where [column] is stored." />
<cfargument name="column" type="string" required="true" hint="I am the name of the column in [table] where the generated key is compared." />
<!--- var scope --->
<cfset var qGetKey = '' />
<!--- query the [table].[column] passed in for the existence of the passed in key --->
<cfquery name="qGetKey" datasource="#variables.instance.datasource.getDSN()#" username="#variables.instance.datasource.getUsername()#" password="#variables.instance.datasource.getPassword()#">
SELECT #ARGUMENTS.column#
FROM #ARGUMENTS.table#
WHERE #ARGUMENTS.column# = <cfqueryparam value="#ARGUMENTS.checkKey#" cfsqltype="cf_sql_varchar" />
</cfquery>
<!--- check if the key exists (there is a record returned) --->
<cfif qGetKey.RecordCount>
<!--- a key of this value already exists in the database, return 'false' --->
<cfreturn false />
<!--- otherwise --->
<cfelse>
<!--- this key does not exist in the database, return 'true' --->
<cfreturn true />
</cfif>
</cffunction>
</cfcomponent>