瀏覽代碼

added /api

version-14
Rushabh Mehta 11 年之前
父節點
當前提交
7a1a4c872a
共有 10 個檔案被更改,包括 283 行新增170 行删除
  1. +13
    -1
      webnotes/__init__.py
  2. +97
    -0
      webnotes/api.py
  3. +4
    -1
      webnotes/app.py
  4. +19
    -19
      webnotes/auth.py
  5. +2
    -2
      webnotes/core/doctype/notification_count/notification_count.py
  6. +11
    -132
      webnotes/handler.py
  7. +19
    -11
      webnotes/model/bean.py
  8. +1
    -2
      webnotes/sessions.py
  9. +102
    -0
      webnotes/utils/response.py
  10. +15
    -2
      webnotes/widgets/reportview.py

+ 13
- 1
webnotes/__init__.py 查看文件

@@ -11,7 +11,7 @@ from werkzeug.local import Local, release_local
from werkzeug.exceptions import NotFound
from MySQLdb import ProgrammingError as SQLError

import os, sys, importlib
import os, sys, importlib, inspect
import json
import semantic_version

@@ -459,6 +459,18 @@ def get_attr(method_string):
modulename = '.'.join(method_string.split('.')[:-1])
methodname = method_string.split('.')[-1]
return getattr(get_module(modulename), methodname)
def call(fn, *args, **kwargs):
if hasattr(fn, 'fnargs'):
fnargs = fn.fnargs
else:
fnargs, varargs, varkw, defaults = inspect.getargspec(fn)

newargs = {}
for a in fnargs:
if a in kwargs:
newargs[a] = kwargs.get(a)
return fn(*args, **newargs)

def make_property_setter(args):
args = _dict(args)


+ 97
- 0
webnotes/api.py 查看文件

@@ -0,0 +1,97 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt

import webnotes
import webnotes.handler
import webnotes.client
import webnotes.widgets.reportview
from webnotes.utils.response import build_response, report_error

def handle():
"""
/api/method/{methodname} will call a whitelisted method
/api/resource/{doctype} will query a table
examples:
?fields=["name", "owner"]
?filters=[["Task", "name", "like", "%005"]]
?limit_start=0
?limit_page_length=20
/api/resource/{doctype}/{name} will point to a resource
GET will return doclist
POST will insert
PUT will update
DELETE will delete
/api/resource/{doctype}/{name}?run_method={method} will run a whitelisted controller method
"""
parts = webnotes.request.path[1:].split("/")
call = doctype = name = None
if len(parts) > 1:
call = parts[1]
if len(parts) > 2:
doctype = parts[2]

if len(parts) > 3:
name = parts[3]
try:
if call=="method":
webnotes.local.form_dict.cmd = doctype
webnotes.handler.handle()
return
elif call=="resource":
if "run_method" in webnotes.local.form_dict:
bean = webnotes.bean(doctype, name)

if webnotes.local.request.method=="GET":
if not bean.has_permission("read"):
webnotes.throw("No Permission", webnotes.PermissionError)
webnotes.local.response.update({"data": bean.run_method(webnotes.local.form_dict.run_method,
**webnotes.local.form_dict)})
if webnotes.local.request.method=="POST":
if not bean.has_permission("write"):
webnotes.throw("No Permission", webnotes.PermissionError)
webnotes.local.response.update({"data":bean.run_method(webnotes.local.form_dict.run_method,
**webnotes.local.form_dict)})
webnotes.conn.commit()

else:
if name:
if webnotes.local.request.method=="GET":
webnotes.local.response.update({
"doclist": webnotes.client.get(doctype,
name)})
if webnotes.local.request.method=="POST":
webnotes.local.response.update({
"doclist": webnotes.client.insert(webnotes.local.form_dict.doclist)})
webnotes.conn.commit()
if webnotes.local.request.method=="PUT":
webnotes.local.response.update({
"doclist":webnotes.client.save(webnotes.local.form_dict.doclist)})
webnotes.conn.commit()
if webnotes.local.request.method=="DELETE":
webnotes.client.delete(doctype, name)
webnotes.local.response.message = "ok"
elif doctype:
if webnotes.local.request.method=="GET":
webnotes.local.response.update({
"data": webnotes.call(webnotes.widgets.reportview.execute,
doctype, **webnotes.local.form_dict)})
else:
raise Exception("Bad API")
else:
raise Exception("Bad API")
except Exception, e:
report_error(500)
build_response()

+ 4
- 1
webnotes/app.py 查看文件

@@ -18,6 +18,7 @@ import mimetypes
import webnotes
import webnotes.handler
import webnotes.auth
import webnotes.api
import webnotes.webutils
from webnotes.utils import get_site_name

@@ -56,8 +57,10 @@ def application(request):
webnotes.local._response = Response()
webnotes.http_request = webnotes.auth.HTTPRequest()

if webnotes.form_dict.cmd:
if webnotes.local.form_dict.cmd:
webnotes.handler.handle()
elif webnotes.request.path.startswith("/api/"):
webnotes.api.handle()
elif webnotes.local.request.method in ('GET', 'HEAD'):
webnotes.webutils.render(webnotes.request.path[1:])
else:


+ 19
- 19
webnotes/auth.py 查看文件

@@ -87,11 +87,11 @@ class HTTPRequest:
class LoginManager:
def __init__(self):
self.user = None
if webnotes.form_dict.get('cmd')=='login':
if webnotes.local.form_dict.get('cmd')=='login' or webnotes.local.request.path=="/api/method/login":
self.login()
else:
self.make_session(resume=True)
def login(self):
# clear cache
webnotes.clear_cache(user = webnotes.form_dict.get('usr'))
@@ -109,16 +109,16 @@ class LoginManager:
info = webnotes.conn.get_value("Profile", self.user,
["user_type", "first_name", "last_name"], as_dict=1)
if info.user_type=="Website User":
webnotes._response.set_cookie("system_user", "no")
webnotes.response["message"] = "No App"
webnotes.local._response.set_cookie("system_user", "no")
webnotes.local.response["message"] = "No App"
else:
webnotes._response.set_cookie("system_user", "yes")
webnotes.response['message'] = 'Logged In'
webnotes.local._response.set_cookie("system_user", "yes")
webnotes.local.response['message'] = 'Logged In'

full_name = " ".join(filter(None, [info.first_name, info.last_name]))
webnotes.response["full_name"] = full_name
webnotes._response.set_cookie("full_name", full_name)
webnotes._response.set_cookie("user_id", self.user)
webnotes.local.response["full_name"] = full_name
webnotes.local._response.set_cookie("full_name", full_name)
webnotes.local._response.set_cookie("user_id", self.user)
def make_session(self, resume=False):
# start session
@@ -154,7 +154,7 @@ class LoginManager:
return user[0][0] # in correct case
def fail(self, message):
webnotes.response['message'] = message
webnotes.local.response['message'] = message
raise webnotes.AuthenticationError
@@ -213,12 +213,12 @@ class LoginManager:

if user == webnotes.session.user:
webnotes.session.sid = ""
webnotes._response.delete_cookie("full_name")
webnotes._response.delete_cookie("user_id")
webnotes._response.delete_cookie("sid")
webnotes._response.set_cookie("full_name", "")
webnotes._response.set_cookie("user_id", "")
webnotes._response.set_cookie("sid", "")
webnotes.local._response.delete_cookie("full_name")
webnotes.local._response.delete_cookie("user_id")
webnotes.local._response.delete_cookie("sid")
webnotes.local._response.set_cookie("full_name", "")
webnotes.local._response.set_cookie("user_id", "")
webnotes.local._response.set_cookie("sid", "")

class CookieManager:
def __init__(self):
@@ -231,9 +231,9 @@ class CookieManager:
# sid expires in 3 days
expires = datetime.datetime.now() + datetime.timedelta(days=3)
if webnotes.session.sid:
webnotes._response.set_cookie("sid", webnotes.session.sid, expires = expires)
webnotes.local._response.set_cookie("sid", webnotes.session.sid, expires = expires)
if webnotes.session.session_country:
webnotes._response.set_cookie('country', webnotes.session.get("session_country"))
webnotes.local._response.set_cookie('country', webnotes.session.get("session_country"))
def set_remember_me(self):
from webnotes.utils import cint
@@ -247,7 +247,7 @@ class CookieManager:
expires = datetime.datetime.now() + \
datetime.timedelta(days=remember_days)

webnotes._response.set_cookie["remember_me"] = 1
webnotes.local._response.set_cookie["remember_me"] = 1


def _update_password(user, password):


+ 2
- 2
webnotes/core/doctype/notification_count/notification_count.py 查看文件

@@ -56,12 +56,12 @@ def delete_notification_count_for(doctype):
def delete_event_notification_count():
delete_notification_count_for("Event")

def clear_doctype_notifications(controller, method=None):
def clear_doctype_notifications(bean, method=None):
if webnotes.flags.in_import:
return
config = get_notification_config()
doctype = controller.doc.doctype
doctype = bean.doc.doctype

if doctype in config.for_doctype:
delete_notification_count_for(doctype)


+ 11
- 132
webnotes/handler.py 查看文件

@@ -2,23 +2,20 @@
# MIT License. See license.txt

from __future__ import unicode_literals
import sys, os
import json
import webnotes
import webnotes.utils
import webnotes.sessions
import webnotes.utils.file_manager
import webnotes.widgets.form.run_method
from webnotes.utils.response import build_response, report_error

@webnotes.whitelist(allow_guest=True)
def startup():
webnotes.response.update(webnotes.sessions.get())

def cleanup_docs():
import webnotes.model.utils
if webnotes.response.get('docs') and type(webnotes.response['docs'])!=dict:
webnotes.response['docs'] = webnotes.model.utils.compress(webnotes.response['docs'])

@webnotes.whitelist()
def runserverobj(arg=None):
import webnotes.widgets.form.run_method
webnotes.widgets.form.run_method.runserverobj()

@webnotes.whitelist(allow_guest=True)
@@ -38,16 +35,12 @@ def run_custom_method(doctype, name, custom_method):
bean = webnotes.bean(doctype, name)
controller = bean.get_controller()
if getattr(controller, custom_method, webnotes._dict()).is_whitelisted:
call(getattr(controller, custom_method), webnotes.local.form_dict)
webnotes.call(getattr(controller, custom_method), **webnotes.local.form_dict)
else:
webnotes.throw("Not Allowed")

@webnotes.whitelist()
def uploadfile():
import webnotes.utils
import webnotes.utils.file_manager
import json

try:
if webnotes.form_dict.get('from_form'):
try:
@@ -67,19 +60,9 @@ def uploadfile():

def handle():
"""handle request"""
cmd = webnotes.form_dict['cmd']
cmd = webnotes.local.form_dict.cmd
def _error(status_code):
webnotes.errprint(webnotes.utils.get_traceback())
webnotes._response.status_code = status_code
if webnotes.request_method == "POST":
webnotes.conn.rollback()

if cmd!='login':
# login executed in webnotes.auth
if webnotes.request_method == "POST":
webnotes.conn.begin()
if cmd!='login':
status_codes = {
webnotes.PermissionError: 403,
webnotes.AuthenticationError: 401,
@@ -91,12 +74,12 @@ def handle():
try:
execute_cmd(cmd)
except Exception, e:
_error(status_codes.get(e.__class__, 500))
report_error(status_codes.get(e.__class__, 500))
else:
if webnotes.request_method == "POST" and webnotes.conn:
if webnotes.local.request.method in ("POST", "PUT") and webnotes.conn:
webnotes.conn.commit()
print_response()
build_response()

if webnotes.conn:
webnotes.conn.close()
@@ -117,7 +100,7 @@ def execute_cmd(cmd):
webnotes.msgprint('Not Allowed, %s' % str(method))
raise webnotes.PermissionError('Not Allowed, %s' % str(method))
ret = call(method, webnotes.form_dict)
ret = webnotes.call(method, **webnotes.form_dict)

# returns with a message
if ret:
@@ -128,20 +111,6 @@ def execute_cmd(cmd):
webnotes.local.session_obj.update()


def call(fn, args):
import inspect

if hasattr(fn, 'fnargs'):
fnargs = fn.fnargs
else:
fnargs, varargs, varkw, defaults = inspect.getargspec(fn)

newargs = {}
for a in fnargs:
if a in args:
newargs[a] = args.get(a)
return fn(**newargs)

def get_attr(cmd):
"""get method object from cmd"""
if '.' in cmd:
@@ -151,93 +120,3 @@ def get_attr(cmd):
webnotes.log("method:" + cmd)
return method
def print_response():
print_map = {
'csv': print_csv,
'download': print_raw,
'json': print_json,
'page': print_page
}
print_map.get(webnotes.response.get('type'), print_json)()

def print_page():
"""print web page"""

from webnotes.webutils import render
render(webnotes.response['page_name'])

def print_json():
make_logs()
cleanup_docs()

webnotes._response.headers["Content-Type"] = "text/json; charset: utf-8"

import json
print_zip(json.dumps(webnotes.local.response, default=json_handler, separators=(',',':')))
def print_csv():
webnotes._response.headers["Content-Type"] = \
"text/csv; charset: utf-8"
webnotes._response.headers["Content-Disposition"] = \
"attachment; filename=%s.csv" % webnotes.response['doctype'].replace(' ', '_')
webnotes._response.data = webnotes.response['result']

def print_raw():
webnotes._response.headers["Content-Type"] = \
mimetypes.guess_type(webnotes.response['filename'])[0] or "application/unknown"
webnotes._response.headers["Content-Disposition"] = \
"filename=%s" % webnotes.response['filename'].replace(' ', '_')
webnotes._response.data = webnotes.response['filecontent']

def make_logs():
"""make strings for msgprint and errprint"""
import json
from webnotes import conf
from webnotes.utils import cstr
if webnotes.error_log:
# webnotes.response['exc'] = json.dumps("\n".join([cstr(d) for d in webnotes.error_log]))
webnotes.response['exc'] = json.dumps([cstr(d) for d in webnotes.local.error_log])

if webnotes.local.message_log:
webnotes.response['_server_messages'] = json.dumps([cstr(d) for d in webnotes.local.message_log])
if webnotes.debug_log and conf.get("logging") or False:
webnotes.response['_debug_messages'] = json.dumps(webnotes.local.debug_log)

def print_zip(response):
response = response.encode('utf-8')
orig_len = len(response)
if accept_gzip() and orig_len>512:
response = compressBuf(response)
webnotes._response.headers["Content-Encoding"] = "gzip"
webnotes._response.headers["Content-Length"] = str(len(response))
webnotes._response.data = response
def json_handler(obj):
"""serialize non-serializable data for json"""
import datetime
from werkzeug.local import LocalProxy
# serialize date
if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime)):
return unicode(obj)
elif isinstance(obj, LocalProxy):
return unicode(obj)
else:
raise TypeError, """Object of type %s with value of %s is not JSON serializable""" % \
(type(obj), repr(obj))

def accept_gzip():
if "gzip" in webnotes.get_request_header("HTTP_ACCEPT_ENCODING", ""):
return True

def compressBuf(buf):
import gzip, cStringIO
zbuf = cStringIO.StringIO()
zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf, compresslevel = 5)
zfile.write(buf)
zfile.close()
return zbuf.getvalue()

+ 19
- 11
webnotes/model/bean.py 查看文件

@@ -220,17 +220,28 @@ class Bean:
idx_map[d.parentfield] = d.idx

def run_method(self, method, *args, **kwargs):
if not args:
args = []
self.make_controller()
def add_to_response(out, new_response):
if isinstance(new_response, dict):
out.update(new_response)
if hasattr(self.controller, method):
getattr(self.controller, method)(*args, **kwargs)
add_to_response(webnotes.local.response, webnotes.call(getattr(self.controller, method), *args, **kwargs))
if hasattr(self.controller, 'custom_' + method):
getattr(self.controller, 'custom_' + method)(*args, **kwargs)
add_to_response(webnotes.local.response, webnotes.call(getattr(self.controller, 'custom_' + method), *args, **kwargs))

args = [self, method] + args
for handler in webnotes.get_hooks("bean_event:" + self.doc.doctype + ":" + method) \
+ webnotes.get_hooks("bean_event:*:" + method):
add_to_response(webnotes.local.response, webnotes.call(webnotes.get_attr(handler), *args, **kwargs))

notify(self, method, *args, **kwargs)
self.set_doclist(self.controller.doclist)
return webnotes.local.response
def get_attr(self, method):
self.make_controller()
return getattr(self.controller, method, None)
@@ -273,7 +284,10 @@ class Bean:
self.set_doclist(new_doclist)

def has_read_perm(self):
return webnotes.has_permission(self.doc.doctype, "read", self.doc)
return self.has_permission("read")
def has_permission(self, permtype):
return webnotes.has_permission(self.doc.doctype, permtype, self.doc)
def save(self, check_links=1, ignore_permissions=None):
if ignore_permissions:
@@ -494,12 +508,6 @@ def clone(source_wrapper):
return new_wrapper

def notify(bean, caller, *args, **kwargs):
for hook in webnotes.get_hooks().bean_event or []:
doctype, trigger, handler = hook.split(":")
if ((doctype=="*") or (doctype==bean.doc.doctype)) and caller==trigger:
webnotes.get_attr(handler)(bean, trigger, *args, **kwargs)

# for bc
def getlist(doclist, parentfield):
import webnotes.model.utils


+ 1
- 2
webnotes/sessions.py 查看文件

@@ -148,8 +148,7 @@ class Session:
data = self.get_session_record()
if data:
# set language
self.data = webnotes._dict({'data': data,
'user':data.user, 'sid': self.sid})
self.data = webnotes._dict({'data': data, 'user':data.user, 'sid': self.sid})
else:
self.start_as_guest()


+ 102
- 0
webnotes/utils/response.py 查看文件

@@ -0,0 +1,102 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt

from __future__ import unicode_literals
import json, inspect
import datetime
import gzip, cStringIO
import webnotes
import webnotes.utils
import webnotes.sessions
import webnotes.model.utils
from werkzeug.local import LocalProxy

def report_error(status_code):
webnotes.errprint(webnotes.utils.get_traceback())
webnotes._response.status_code = status_code
if webnotes.request_method == "POST":
webnotes.conn.rollback()

def build_response():
print_map = {
'csv': print_csv,
'download': print_raw,
'json': print_json,
'page': print_page
}
print_map.get(webnotes.response.get('type'), print_json)()

def print_page():
"""print web page"""
from webnotes.webutils import render
render(webnotes.response['page_name'])

def print_json():
make_logs()
cleanup_docs()
webnotes._response.headers["Content-Type"] = "text/json; charset: utf-8"
print_zip(json.dumps(webnotes.local.response, default=json_handler, separators=(',',':')))

def cleanup_docs():
if webnotes.response.get('docs') and type(webnotes.response['docs'])!=dict:
webnotes.response['docs'] = webnotes.model.utils.compress(webnotes.response['docs'])
def print_csv():
webnotes._response.headers["Content-Type"] = \
"text/csv; charset: utf-8"
webnotes._response.headers["Content-Disposition"] = \
"attachment; filename=%s.csv" % webnotes.response['doctype'].replace(' ', '_')
webnotes._response.data = webnotes.response['result']

def print_raw():
webnotes._response.headers["Content-Type"] = \
mimetypes.guess_type(webnotes.response['filename'])[0] or "application/unknown"
webnotes._response.headers["Content-Disposition"] = \
"filename=%s" % webnotes.response['filename'].replace(' ', '_')
webnotes._response.data = webnotes.response['filecontent']

def make_logs():
"""make strings for msgprint and errprint"""
if webnotes.error_log:
# webnotes.response['exc'] = json.dumps("\n".join([cstr(d) for d in webnotes.error_log]))
webnotes.response['exc'] = json.dumps([webnotes.utils.cstr(d) for d in webnotes.local.error_log])

if webnotes.local.message_log:
webnotes.response['_server_messages'] = json.dumps([webnotes.utils.cstr(d) for d in webnotes.local.message_log])
if webnotes.debug_log and webnotes.conf.get("logging") or False:
webnotes.response['_debug_messages'] = json.dumps(webnotes.local.debug_log)

def print_zip(response):
response = response.encode('utf-8')
orig_len = len(response)
if accept_gzip() and orig_len>512:
response = compressBuf(response)
webnotes._response.headers["Content-Encoding"] = "gzip"
webnotes._response.headers["Content-Length"] = str(len(response))
webnotes._response.data = response
def json_handler(obj):
"""serialize non-serializable data for json"""
# serialize date
if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime)):
return unicode(obj)
elif isinstance(obj, LocalProxy):
return unicode(obj)
else:
raise TypeError, """Object of type %s with value of %s is not JSON serializable""" % \
(type(obj), repr(obj))

def accept_gzip():
if "gzip" in webnotes.get_request_header("HTTP_ACCEPT_ENCODING", ""):
return True

def compressBuf(buf):
zbuf = cStringIO.StringIO()
zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf, compresslevel = 5)
zfile.write(buf)
zfile.close()
return zbuf.getvalue()

+ 15
- 2
webnotes/widgets/reportview.py 查看文件

@@ -29,12 +29,25 @@ def get_form_params():
def execute(doctype, query=None, filters=None, fields=None, docstatus=None,
group_by=None, order_by=None, limit_start=0, limit_page_length=None,
as_list=False, with_childnames=False, debug=False):
"""
fields as list ["name", "owner"] or ["tabTask.name", "tabTask.owner"]
filters as list of list [["Task", "name", "=", "TASK00001"]]
"""

if query:
return run_custom_query(query)
if not filters: filters = []
if not docstatus: docstatus = []
if not filters:
filters = []
if isinstance(filters, basestring):
filters = json.loads(filters)
if not docstatus:
docstatus = []
if not fields:
fields = ["name"]
if isinstance(fields, basestring):
filters = json.loads(fields)

args = prepare_args(doctype, filters, fields, docstatus, group_by, order_by, with_childnames)
args.limit = add_limit(limit_start, limit_page_length)


Loading…
取消
儲存