|
796dcfe…
|
drh
|
1 |
#ifdef FOSSIL_ENABLE_JSON |
|
796dcfe…
|
drh
|
2 |
/* |
|
c19f34c…
|
drh
|
3 |
** Copyright (c) 2011 D. Richard Hipp |
|
796dcfe…
|
drh
|
4 |
** |
|
796dcfe…
|
drh
|
5 |
** This program is free software; you can redistribute it and/or |
|
796dcfe…
|
drh
|
6 |
** modify it under the terms of the Simplified BSD License (also |
|
796dcfe…
|
drh
|
7 |
** known as the "2-Clause License" or "FreeBSD License".) |
|
796dcfe…
|
drh
|
8 |
** |
|
796dcfe…
|
drh
|
9 |
** This program is distributed in the hope that it will be useful, |
|
796dcfe…
|
drh
|
10 |
** but without any warranty; without even the implied warranty of |
|
796dcfe…
|
drh
|
11 |
** merchantability or fitness for a particular purpose. |
|
796dcfe…
|
drh
|
12 |
** |
|
796dcfe…
|
drh
|
13 |
** Author contact information: |
|
796dcfe…
|
drh
|
14 |
** [email protected] |
|
796dcfe…
|
drh
|
15 |
** http://www.hwaci.com/drh/ |
|
796dcfe…
|
drh
|
16 |
** |
|
796dcfe…
|
drh
|
17 |
*/ |
|
796dcfe…
|
drh
|
18 |
|
|
796dcfe…
|
drh
|
19 |
#include "config.h" |
|
796dcfe…
|
drh
|
20 |
#include "json_report.h" |
|
796dcfe…
|
drh
|
21 |
|
|
796dcfe…
|
drh
|
22 |
#if INTERFACE |
|
796dcfe…
|
drh
|
23 |
#include "json_detail.h" |
|
796dcfe…
|
drh
|
24 |
#endif |
|
796dcfe…
|
drh
|
25 |
|
|
796dcfe…
|
drh
|
26 |
|
|
a80f274…
|
stephan
|
27 |
static cson_value * json_report_create(void); |
|
a80f274…
|
stephan
|
28 |
static cson_value * json_report_get(void); |
|
a80f274…
|
stephan
|
29 |
static cson_value * json_report_list(void); |
|
a80f274…
|
stephan
|
30 |
static cson_value * json_report_run(void); |
|
a80f274…
|
stephan
|
31 |
static cson_value * json_report_save(void); |
|
796dcfe…
|
drh
|
32 |
|
|
796dcfe…
|
drh
|
33 |
/* |
|
796dcfe…
|
drh
|
34 |
** Mapping of /json/report/XXX commands/paths to callbacks. |
|
796dcfe…
|
drh
|
35 |
*/ |
|
796dcfe…
|
drh
|
36 |
static const JsonPageDef JsonPageDefs_Report[] = { |
|
796dcfe…
|
drh
|
37 |
{"create", json_report_create, 0}, |
|
796dcfe…
|
drh
|
38 |
{"get", json_report_get, 0}, |
|
796dcfe…
|
drh
|
39 |
{"list", json_report_list, 0}, |
|
796dcfe…
|
drh
|
40 |
{"run", json_report_run, 0}, |
|
796dcfe…
|
drh
|
41 |
{"save", json_report_save, 0}, |
|
796dcfe…
|
drh
|
42 |
/* Last entry MUST have a NULL name. */ |
|
796dcfe…
|
drh
|
43 |
{NULL,NULL,0} |
|
796dcfe…
|
drh
|
44 |
}; |
|
796dcfe…
|
drh
|
45 |
/* |
|
796dcfe…
|
drh
|
46 |
** Implementation of the /json/report page. |
|
796dcfe…
|
drh
|
47 |
** |
|
796dcfe…
|
drh
|
48 |
** |
|
796dcfe…
|
drh
|
49 |
*/ |
|
a80f274…
|
stephan
|
50 |
cson_value * json_page_report(void){ |
|
796dcfe…
|
drh
|
51 |
if(!g.perm.RdTkt && !g.perm.NewTkt ){ |
|
796dcfe…
|
drh
|
52 |
json_set_err(FSL_JSON_E_DENIED, |
|
796dcfe…
|
drh
|
53 |
"Requires 'r' or 'n' permissions."); |
|
796dcfe…
|
drh
|
54 |
return NULL; |
|
796dcfe…
|
drh
|
55 |
} |
|
055dfb1…
|
stephan
|
56 |
return json_page_dispatch_helper(JsonPageDefs_Report); |
|
796dcfe…
|
drh
|
57 |
} |
|
796dcfe…
|
drh
|
58 |
|
|
796dcfe…
|
drh
|
59 |
/* |
|
796dcfe…
|
drh
|
60 |
** Searches the environment for a "report" parameter |
|
796dcfe…
|
drh
|
61 |
** (CLI: -report/-r #). |
|
796dcfe…
|
drh
|
62 |
** |
|
796dcfe…
|
drh
|
63 |
** If one is not found and argPos is >0 then json_command_arg() |
|
796dcfe…
|
drh
|
64 |
** is checked. |
|
bf9669f…
|
andygoth
|
65 |
** |
|
796dcfe…
|
drh
|
66 |
** Returns >0 (the report number) on success . |
|
796dcfe…
|
drh
|
67 |
*/ |
|
796dcfe…
|
drh
|
68 |
static int json_report_get_number(int argPos){ |
|
796dcfe…
|
drh
|
69 |
int nReport = json_find_option_int("report",NULL,"r",-1); |
|
796dcfe…
|
drh
|
70 |
if( (nReport<=0) && cson_value_is_integer(g.json.reqPayload.v)){ |
|
796dcfe…
|
drh
|
71 |
nReport = cson_value_get_integer(g.json.reqPayload.v); |
|
796dcfe…
|
drh
|
72 |
} |
|
796dcfe…
|
drh
|
73 |
if( (nReport <= 0) && (argPos>0) ){ |
|
796dcfe…
|
drh
|
74 |
char const * arg = json_command_arg(argPos); |
|
796dcfe…
|
drh
|
75 |
if(arg && fossil_isdigit(*arg)) { |
|
796dcfe…
|
drh
|
76 |
nReport = atoi(arg); |
|
796dcfe…
|
drh
|
77 |
} |
|
796dcfe…
|
drh
|
78 |
} |
|
796dcfe…
|
drh
|
79 |
return nReport; |
|
796dcfe…
|
drh
|
80 |
} |
|
796dcfe…
|
drh
|
81 |
|
|
a80f274…
|
stephan
|
82 |
static cson_value * json_report_create(void){ |
|
bbcc8fe…
|
stephan
|
83 |
json_set_err(FSL_JSON_E_NYI, NULL); |
|
796dcfe…
|
drh
|
84 |
return NULL; |
|
796dcfe…
|
drh
|
85 |
} |
|
796dcfe…
|
drh
|
86 |
|
|
a80f274…
|
stephan
|
87 |
static cson_value * json_report_get(void){ |
|
796dcfe…
|
drh
|
88 |
int nReport; |
|
796dcfe…
|
drh
|
89 |
Stmt q = empty_Stmt; |
|
796dcfe…
|
drh
|
90 |
cson_value * pay = NULL; |
|
796dcfe…
|
drh
|
91 |
|
|
796dcfe…
|
drh
|
92 |
if(!g.perm.TktFmt){ |
|
796dcfe…
|
drh
|
93 |
json_set_err(FSL_JSON_E_DENIED, |
|
796dcfe…
|
drh
|
94 |
"Requires 't' privileges."); |
|
796dcfe…
|
drh
|
95 |
return NULL; |
|
796dcfe…
|
drh
|
96 |
} |
|
796dcfe…
|
drh
|
97 |
nReport = json_report_get_number(3); |
|
796dcfe…
|
drh
|
98 |
if(nReport <=0){ |
|
796dcfe…
|
drh
|
99 |
json_set_err(FSL_JSON_E_MISSING_ARGS, |
|
53ba1f2…
|
stephan
|
100 |
"Missing or invalid 'report' (-r) parameter."); |
|
796dcfe…
|
drh
|
101 |
return NULL; |
|
796dcfe…
|
drh
|
102 |
} |
|
796dcfe…
|
drh
|
103 |
|
|
796dcfe…
|
drh
|
104 |
db_prepare(&q,"SELECT rn AS report," |
|
796dcfe…
|
drh
|
105 |
" owner AS owner," |
|
796dcfe…
|
drh
|
106 |
" title AS title," |
|
070b755…
|
stephan
|
107 |
" cast(strftime('%%s',mtime) as int) as timestamp," |
|
796dcfe…
|
drh
|
108 |
" cols as columns," |
|
796dcfe…
|
drh
|
109 |
" sqlcode as sqlCode" |
|
796dcfe…
|
drh
|
110 |
" FROM reportfmt" |
|
796dcfe…
|
drh
|
111 |
" WHERE rn=%d", |
|
796dcfe…
|
drh
|
112 |
nReport); |
|
796dcfe…
|
drh
|
113 |
if( SQLITE_ROW != db_step(&q) ){ |
|
796dcfe…
|
drh
|
114 |
db_finalize(&q); |
|
796dcfe…
|
drh
|
115 |
json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
|
796dcfe…
|
drh
|
116 |
"Report #%d not found.", nReport); |
|
796dcfe…
|
drh
|
117 |
return NULL; |
|
796dcfe…
|
drh
|
118 |
} |
|
796dcfe…
|
drh
|
119 |
pay = cson_sqlite3_row_to_object(q.pStmt); |
|
796dcfe…
|
drh
|
120 |
db_finalize(&q); |
|
796dcfe…
|
drh
|
121 |
return pay; |
|
796dcfe…
|
drh
|
122 |
} |
|
796dcfe…
|
drh
|
123 |
|
|
796dcfe…
|
drh
|
124 |
/* |
|
796dcfe…
|
drh
|
125 |
** Impl of /json/report/list. |
|
796dcfe…
|
drh
|
126 |
*/ |
|
a80f274…
|
stephan
|
127 |
static cson_value * json_report_list(void){ |
|
796dcfe…
|
drh
|
128 |
Blob sql = empty_blob; |
|
796dcfe…
|
drh
|
129 |
cson_value * pay = NULL; |
|
796dcfe…
|
drh
|
130 |
if(!g.perm.RdTkt){ |
|
796dcfe…
|
drh
|
131 |
json_set_err(FSL_JSON_E_DENIED, |
|
796dcfe…
|
drh
|
132 |
"Requires 'r' privileges."); |
|
796dcfe…
|
drh
|
133 |
return NULL; |
|
796dcfe…
|
drh
|
134 |
} |
|
796dcfe…
|
drh
|
135 |
blob_append(&sql, "SELECT" |
|
796dcfe…
|
drh
|
136 |
" rn AS report," |
|
796dcfe…
|
drh
|
137 |
" title as title," |
|
796dcfe…
|
drh
|
138 |
" owner as owner" |
|
796dcfe…
|
drh
|
139 |
" FROM reportfmt" |
|
796dcfe…
|
drh
|
140 |
" WHERE 1" |
|
796dcfe…
|
drh
|
141 |
" ORDER BY title", |
|
796dcfe…
|
drh
|
142 |
-1); |
|
796dcfe…
|
drh
|
143 |
pay = json_sql_to_array_of_obj(&sql, NULL, 1); |
|
796dcfe…
|
drh
|
144 |
if(!pay){ |
|
796dcfe…
|
drh
|
145 |
json_set_err(FSL_JSON_E_UNKNOWN, |
|
796dcfe…
|
drh
|
146 |
"Quite unexpected: no ticket reports found."); |
|
796dcfe…
|
drh
|
147 |
} |
|
796dcfe…
|
drh
|
148 |
return pay; |
|
796dcfe…
|
drh
|
149 |
} |
|
796dcfe…
|
drh
|
150 |
|
|
796dcfe…
|
drh
|
151 |
/* |
|
796dcfe…
|
drh
|
152 |
** Impl for /json/report/run |
|
796dcfe…
|
drh
|
153 |
** |
|
796dcfe…
|
drh
|
154 |
** Options/arguments: |
|
796dcfe…
|
drh
|
155 |
** |
|
796dcfe…
|
drh
|
156 |
** report=int (CLI: -report # or -r #) is the report number to run. |
|
796dcfe…
|
drh
|
157 |
** |
|
796dcfe…
|
drh
|
158 |
** limit=int (CLI: -limit # or -n #) -n is for compat. with other commands. |
|
796dcfe…
|
drh
|
159 |
** |
|
e2bdc10…
|
danield
|
160 |
** format=a|o Specifies result format: a=each row is an array, o=each |
|
796dcfe…
|
drh
|
161 |
** row is an object. Default=o. |
|
796dcfe…
|
drh
|
162 |
*/ |
|
a80f274…
|
stephan
|
163 |
static cson_value * json_report_run(void){ |
|
796dcfe…
|
drh
|
164 |
int nReport; |
|
796dcfe…
|
drh
|
165 |
Stmt q = empty_Stmt; |
|
796dcfe…
|
drh
|
166 |
cson_object * pay = NULL; |
|
796dcfe…
|
drh
|
167 |
cson_array * tktList = NULL; |
|
796dcfe…
|
drh
|
168 |
char const * zFmt; |
|
796dcfe…
|
drh
|
169 |
char * zTitle = NULL; |
|
796dcfe…
|
drh
|
170 |
Blob sql = empty_blob; |
|
796dcfe…
|
drh
|
171 |
int limit = 0; |
|
796dcfe…
|
drh
|
172 |
cson_value * colNames = NULL; |
|
796dcfe…
|
drh
|
173 |
int i; |
|
796dcfe…
|
drh
|
174 |
|
|
796dcfe…
|
drh
|
175 |
if(!g.perm.RdTkt){ |
|
796dcfe…
|
drh
|
176 |
json_set_err(FSL_JSON_E_DENIED, |
|
796dcfe…
|
drh
|
177 |
"Requires 'r' privileges."); |
|
796dcfe…
|
drh
|
178 |
return NULL; |
|
796dcfe…
|
drh
|
179 |
} |
|
796dcfe…
|
drh
|
180 |
nReport = json_report_get_number(3); |
|
796dcfe…
|
drh
|
181 |
if(nReport <=0){ |
|
796dcfe…
|
drh
|
182 |
json_set_err(FSL_JSON_E_MISSING_ARGS, |
|
796dcfe…
|
drh
|
183 |
"Missing or invalid 'number' (-n) parameter."); |
|
796dcfe…
|
drh
|
184 |
goto error; |
|
796dcfe…
|
drh
|
185 |
} |
|
796dcfe…
|
drh
|
186 |
zFmt = json_find_option_cstr2("format",NULL,"f",3); |
|
796dcfe…
|
drh
|
187 |
if(!zFmt) zFmt = "o"; |
|
796dcfe…
|
drh
|
188 |
db_prepare(&q, |
|
796dcfe…
|
drh
|
189 |
"SELECT sqlcode, " |
|
796dcfe…
|
drh
|
190 |
" title" |
|
796dcfe…
|
drh
|
191 |
" FROM reportfmt" |
|
796dcfe…
|
drh
|
192 |
" WHERE rn=%d", |
|
796dcfe…
|
drh
|
193 |
nReport); |
|
796dcfe…
|
drh
|
194 |
if(SQLITE_ROW != db_step(&q)){ |
|
796dcfe…
|
drh
|
195 |
json_set_err(FSL_JSON_E_INVALID_ARGS, |
|
796dcfe…
|
drh
|
196 |
"Report number %d not found.", |
|
796dcfe…
|
drh
|
197 |
nReport); |
|
796dcfe…
|
drh
|
198 |
db_finalize(&q); |
|
796dcfe…
|
drh
|
199 |
goto error; |
|
796dcfe…
|
drh
|
200 |
} |
|
796dcfe…
|
drh
|
201 |
|
|
796dcfe…
|
drh
|
202 |
limit = json_find_option_int("limit",NULL,"n",-1); |
|
796dcfe…
|
drh
|
203 |
|
|
bf9669f…
|
andygoth
|
204 |
|
|
796dcfe…
|
drh
|
205 |
/* Copy over report's SQL...*/ |
|
796dcfe…
|
drh
|
206 |
blob_append(&sql, db_column_text(&q,0), -1); |
|
4c3e172…
|
danield
|
207 |
zTitle = fossil_strdup(db_column_text(&q,1)); |
|
796dcfe…
|
drh
|
208 |
db_finalize(&q); |
|
49b0ff1…
|
drh
|
209 |
db_prepare(&q, "%s", blob_sql_text(&sql)); |
|
796dcfe…
|
drh
|
210 |
|
|
796dcfe…
|
drh
|
211 |
/** Build the response... */ |
|
796dcfe…
|
drh
|
212 |
pay = cson_new_object(); |
|
796dcfe…
|
drh
|
213 |
|
|
796dcfe…
|
drh
|
214 |
cson_object_set(pay, "report", json_new_int(nReport)); |
|
796dcfe…
|
drh
|
215 |
cson_object_set(pay, "title", json_new_string(zTitle)); |
|
796dcfe…
|
drh
|
216 |
if(limit>0){ |
|
796dcfe…
|
drh
|
217 |
cson_object_set(pay, "limit", json_new_int((limit<0) ? 0 : limit)); |
|
796dcfe…
|
drh
|
218 |
} |
|
796dcfe…
|
drh
|
219 |
free(zTitle); |
|
796dcfe…
|
drh
|
220 |
zTitle = NULL; |
|
796dcfe…
|
drh
|
221 |
|
|
796dcfe…
|
drh
|
222 |
if(g.perm.TktFmt){ |
|
796dcfe…
|
drh
|
223 |
cson_object_set(pay, "sqlcode", |
|
796dcfe…
|
drh
|
224 |
cson_value_new_string(blob_str(&sql), |
|
796dcfe…
|
drh
|
225 |
(unsigned int)blob_size(&sql))); |
|
796dcfe…
|
drh
|
226 |
} |
|
796dcfe…
|
drh
|
227 |
blob_reset(&sql); |
|
796dcfe…
|
drh
|
228 |
|
|
796dcfe…
|
drh
|
229 |
colNames = cson_sqlite3_column_names(q.pStmt); |
|
796dcfe…
|
drh
|
230 |
cson_object_set( pay, "columnNames", colNames); |
|
796dcfe…
|
drh
|
231 |
for( i = 0 ; ((limit>0) ?(i < limit) : 1) |
|
796dcfe…
|
drh
|
232 |
&& (SQLITE_ROW == db_step(&q)); |
|
796dcfe…
|
drh
|
233 |
++i){ |
|
796dcfe…
|
drh
|
234 |
cson_value * row = ('a'==*zFmt) |
|
796dcfe…
|
drh
|
235 |
? cson_sqlite3_row_to_array(q.pStmt) |
|
796dcfe…
|
drh
|
236 |
: cson_sqlite3_row_to_object2(q.pStmt, |
|
796dcfe…
|
drh
|
237 |
cson_value_get_array(colNames)); |
|
796dcfe…
|
drh
|
238 |
; |
|
796dcfe…
|
drh
|
239 |
if(row && !tktList){ |
|
796dcfe…
|
drh
|
240 |
tktList = cson_new_array(); |
|
796dcfe…
|
drh
|
241 |
} |
|
796dcfe…
|
drh
|
242 |
cson_array_append(tktList, row); |
|
796dcfe…
|
drh
|
243 |
} |
|
796dcfe…
|
drh
|
244 |
db_finalize(&q); |
|
796dcfe…
|
drh
|
245 |
cson_object_set(pay, "tickets", |
|
796dcfe…
|
drh
|
246 |
tktList ? cson_array_value(tktList) : cson_value_null()); |
|
796dcfe…
|
drh
|
247 |
|
|
796dcfe…
|
drh
|
248 |
goto end; |
|
796dcfe…
|
drh
|
249 |
|
|
796dcfe…
|
drh
|
250 |
error: |
|
796dcfe…
|
drh
|
251 |
assert(0 != g.json.resultCode); |
|
796dcfe…
|
drh
|
252 |
cson_value_free( cson_object_value(pay) ); |
|
796dcfe…
|
drh
|
253 |
pay = NULL; |
|
055dfb1…
|
stephan
|
254 |
end: |
|
796dcfe…
|
drh
|
255 |
|
|
796dcfe…
|
drh
|
256 |
return pay ? cson_object_value(pay) : NULL; |
|
796dcfe…
|
drh
|
257 |
|
|
796dcfe…
|
drh
|
258 |
} |
|
796dcfe…
|
drh
|
259 |
|
|
a80f274…
|
stephan
|
260 |
static cson_value * json_report_save(void){ |
|
796dcfe…
|
drh
|
261 |
return NULL; |
|
796dcfe…
|
drh
|
262 |
} |
|
796dcfe…
|
drh
|
263 |
#endif /* FOSSIL_ENABLE_JSON */ |