python3 compatibility + webapp to flask refactoring

This commit is contained in:
Kaan Soral
2024-01-27 20:37:44 +03:00
parent 5180a9c31c
commit 53b3de28ab
81 changed files with 11213 additions and 1085 deletions
View File
+7
View File
@@ -8,9 +8,16 @@
/node/node_modules/
/scripts/node_modules/
*.pyc
*.pyo
*.pem
*.pub
*.bak
/scripts/test.js
# System Files
.DS_Store
Thumbs.db
/venv/
/Resources/
/lib/
/include/
*.cfg
+2 -1
View File
@@ -49,10 +49,11 @@ pip2.7 check
For MacOS pyenv makes sense as it prevents clash between Python versions and allows local versioning by just adding a .python-version to the folder https://github.com/pyenv/pyenv
Make sure to install lxml afterwards:
Make sure to install lxml and Flask afterwards:
```sh
pip2.7 install lxml
pip2.7 install flask -t lib
```
Run the Python2.7 backend - includes an HTTP server, datastore, various utilities - emulates Google App Engine:
+168 -197
View File
@@ -1,218 +1,189 @@
from config import *
from functions import *
class Renderer(webapp.RequestHandler):
@ndb.toplevel
def get(self):
whtml(self,"utility/htmls/renderer.html",domain=gdi(self))
def post(self): self.get()
@app.route('/admin/make')
@ndb.toplevel
def serve_admin_maker():
domain=gdi(request); user=get_user(request,domain)
if not user or user.info.email!="your_email_here": return "no permission"
user.admin=True
user.put()
return "done!"
class Events(webapp.RequestHandler):
@ndb.toplevel
def get(self,tag):
cursor=gdmul(self,"cursor")
if not tag: tag="noteworthy"
if tag=="all": events=Event.query().order(-Event.created)
else: events=Event.query(Event.tag==tag).order(-Event.created)
if cursor: events,ncursor,more=events.fetch_page(100,start_cursor=ndb.Cursor(urlsafe=cursor))
else: events,ncursor,more=events.fetch_page(100)
ncursor=more and ncursor.urlsafe()
whtml(self,"utility/htmls/events.html",domain=gdi(self),events=events,ncursor=ncursor)
def post(self,tag): self.get(tag)
@app.route('/admin/renderer')
@ndb.toplevel
def serve_render():
domain=gdi(request); user=get_user(request,domain)
if not user or not getattr(user,"admin",False): return "no permission"
return shtml("utility/htmls/renderer.html",domain=gdi(request))
@app.route('/admin/events/<tag>')
@ndb.toplevel
def serve_events(tag):
domain=gdi(request); user=get_user(request,domain)
if not user or not getattr(user,"admin",False): return "no permission"
cursor=gdmul(request,"cursor")
if not tag: tag="noteworthy"
if tag=="all": events=Event.query().order(-Event.created)
else: events=Event.query(Event.tag==tag).order(-Event.created)
if cursor: events,ncursor,more=events.fetch_page(100,start_cursor=ndb.Cursor(urlsafe=cursor))
else: events,ncursor,more=events.fetch_page(100)
ncursor=more and ncursor.urlsafe()
return whtml(request,"utility/htmls/events.html",domain=gdi(request),events=events,ncursor=ncursor)
def executor_task(the_code):
self=None
request=None
try:
exec(the_code)
except:
#admin_email(domain,"executor_task failed")
log_trace()
class Executor(webapp.RequestHandler):
@ndb.toplevel
def get(self):
whtml(self,"utility/htmls/executor.html",domain=gdi(self))
@ndb.toplevel
def post(self):
the_none="the_none_output_48"
the_code,is_task=gdmul(self,"code","is_task"); output=the_none; json_output=None; json_size=0; html_output=the_none; output_html=the_none; inspect=the_none; ess=datetime.now()
logging=custom_logging()
#logging=code_logs
if is_task:
deferred.defer(executor_task,the_code,_queue="default")
output="executor_task triggered"
else:
try:
exec(the_code)
except DeadlineExceededError: logging.error("Deadline Exceeded")
except:
log_trace_i("",logging)
if json_output!=None: json_size=olen(json_output); json_output=json.dumps(recursive_datetime_conversion(json_output))
if html_output!=the_none: output_html=html_output
whtml(self,"utility/htmls/executor_output.html",logs=logging.output(),created=datetime.now(),ess_ms=mssince(ess),
output=output,json=json_output,json_size=json_size,html_output=output_html,inspector_output=(inspect!=the_none and ginspect(inspect,force_inspection=1)))
@app.route('/admin/executor',methods=['GET'])
@ndb.toplevel
def serve_executor_get():
domain=gdi(request); user=get_user(request,domain)
if not user or not getattr(user,"admin",False): return "no permission"
return whtml(request,"utility/htmls/executor.html",domain=gdi(request))
class CommunityMapEditor(webapp.RequestHandler):
@ndb.toplevel
def get(self,name):
if not name: return
name=name.split("/")[0]
#if not ((name in community_maps) or name.startswith("jayson_")): return
domain=gdi(self); user=get_user(self,domain)
#if name.startswith("jayson_") and (not user or (user.k()+"") not in inner_circle): return
map=get_by_iid("map|%s"%name)
whtml(self,"utility/htmls/map_editor.html",domain=domain,name="main",map=map,tilesets=tilesets,community=1)
@ndb.toplevel
def post(self,name):
data=self.request.get("data")
name=name.split("/")[0]
if not ((name in community_maps) or name.startswith("jayson_")): return self.response.out.write("Not Permitted!")
domain=gdi(self); user=get_user(self,domain)
if name.startswith("jayson_") and (not user or (user.k()+"") not in inner_circle): return self.response.out.write("Not Permitted!")
map=get_by_iid("map|%s"%name)
if not map: map=Map(key=ndb.Key(Map,name),info=GG())
map.info.data=json.loads(data)
process_map(map)
map.updated=datetime.now()
map.put()
copy_map(name,"test")
self.response.out.write(to_pretty_num(olen(map.info.data)))
class ArtistMapEditor(webapp.RequestHandler):
# from /admin/executor set the user.info.map_editor flag to True for artists, this also unlocks the interface in the game menu
@ndb.toplevel
def get(self,name):
if not name: return
name=name.split("/")[0]
domain=gdi(self); user=get_user(self,domain)
if not user or not gf(user,"map_editor"): return self.response.out.write("Not Permitted!")
map=get_by_iid("map|%s"%name)
whtml(self,"utility/htmls/map_editor.html",domain=domain,name="main",map=map,tilesets=tilesets,community=1)
@ndb.toplevel
def post(self,name):
data=self.request.get("data")
name=name.split("/")[0]
domain=gdi(self); user=get_user(self,domain)
if not user or not gf(user,"map_editor"): return self.response.out.write("Not Permitted!")
map=get_by_iid("map|%s"%name)
if not map: map=Map(key=ndb.Key(Map,name),info=GG())
map.info.data=json.loads(data)
process_map(map)
map.updated=datetime.now()
map.put()
copy_map(name,"test")
self.response.out.write(to_pretty_num(olen(map.info.data)))
class MapEditor(webapp.RequestHandler):
@ndb.toplevel
def get(self,name):
if not name: self.redirect("/admin/map_editor/main"); return
name=name.split("/")[0]
map=get_by_iid("map|%s"%name)
whtml(self,"utility/htmls/map_editor.html",domain=gdi(self),name="main",map=map,tilesets=tilesets)
@ndb.toplevel
def post(self,name):
data=self.request.get("data")
name=name.split("/")[0]
map=get_by_iid("map|%s"%name)
if not map: map=Map(key=ndb.Key(Map,name),info=GG())
map.info.data=json.loads(data)
process_map(map)
map.updated=datetime.now()
map.put()
copy_map(name,"test")
self.response.out.write(to_pretty_num(olen(map.info.data)))
class Selector(webapp.RequestHandler): #NOTICE: << Publicly available at /communityselector! >> [10/10/16]
@ndb.toplevel
def get(self,name):
if not name:
whtml(self,"utility/htmls/imagesets/select-imageset.html",domain=gdi(self),imagesets=imagesets)
return
name=name.split("/")[0]
file=imagesets[name]["file"]
size=imagesets[name]["size"]
width=imagesets[name]["columns"]*size
height=imagesets[name]["rows"]*size
xs=[]; ys=[]
for i in xrange(width/size): xs.append(i)
for j in xrange(height/size): ys.append(j)
whtml(self,"utility/htmls/imagesets/selector.html",domain=gdi(self),name=name,file=file,size=size,width=width,height=height,xs=xs,ys=ys,scale=3)
def post(self,name): self.get(name)
class PhotoUpload(blobstore_handlers.BlobstoreUploadHandler):
@ndb.toplevel
def post(self):
domain=gdi(self); user=get_user(self,domain)
if security_threat(self,domain): return
upload_files = self.get_file_infos('image'); file_info = upload_files[0]; blob_key=blobstore.create_gs_key_async(file_info.gs_object_name)
key=gdmul(self,"key");
logging.info("Image Upload %s"%(key))
upload=get_by_iid("upload|%s"%key); blob_key=blob_key.get_result()
if not upload: upload=Upload(key=ndb.Key(Upload,key),info=GG())
@app.route('/admin/executor',methods=['POST'])
@ndb.toplevel
def serve_executor_post():
domain=gdi(request); user=get_user(request,domain)
if not user or not getattr(user,"admin",False): return "no permission"
the_none="the_none_output_48"
the_code,is_task=gdmul(request,"code","is_task"); output=the_none; json_output=None; json_size=0; html_output=the_none; output_html=the_none; inspect=the_none; ess=datetime.now()
logging=custom_logging()
#logging=code_logs
if is_task:
deferred.defer(executor_task,the_code,_queue="default")
output="executor_task triggered"
else:
try:
exec(the_code)
except DeadlineExceededError: logging.error("Deadline Exceeded")
except:
log_trace_i("",logging)
if json_output!=None: json_size=olen(json_output); json_output=json.dumps(recursive_datetime_conversion(json_output))
if html_output!=the_none: output_html=html_output
return whtml(request,"utility/htmls/executor_output.html",logs=logging.output(),created=datetime.now(),ess_ms=mssince(ess),
output=output,json=json_output,json_size=json_size,html_output=output_html,inspector_output=(inspect!=the_none and ginspect(inspect,force_inspection=1)))
if file_info.content_type in ["image/jpg","image/jpeg","image/jpe","image/png","image/gif"]:
if gf(upload,"blob_key"):
try:
blobstore.delete(blob_keys=upload.info.blob_key)
logging.info("Old image deleted")
except: log_trace()
@app.route('/communitymaps/<name>',methods=['GET'])
def serve_community_maps_get(name=""):
if not name: return "no map"
name=name.split("/")[0]
domain=gdi(request); user=get_user(request,domain)
map=get_by_iid("map|%s"%name)
return whtml(request,"utility/htmls/map_editor.html",domain=domain,name="main",map=map,tilesets=tilesets,community=1)
url=get_serving_url(blob_key,size=images.IMG_SERVING_SIZES_LIMIT)
size=get_image_size(blob_key,encoding=filetype_to_image_encoding(file_info.content_type))
upload.info.blob_key=blob_key
upload.info.content_type=file_info.content_type
upload.info.gs_object_name=file_info.gs_object_name
upload.info.url=url
upload.info.width=size.width
upload.info.height=size.height
@ndb.toplevel
@app.route('/communitymaps/<name>',methods=['POST'])
def serve_community_maps_post(name=""):
data=request.values.get("data")
name=name.split("/")[0]
if not ((name in community_maps) or name.startswith("jayson_")): return make_response("Not Permitted!")
domain=gdi(request); user=get_user(request,domain)
if name.startswith("jayson_") and (not user or (user.k()+"") not in inner_circle): return make_response("Not Permitted!")
map=get_by_iid("map|%s"%name)
if not map: map=Map(key=ndb.Key(Map,name),info=GG())
map.info.data=json.loads(data)
process_map(map)
map.updated=datetime.now()
map.put()
copy_map(name,"test")
return make_response(to_pretty_num(olen(map.info.data)))
self.response.out.write("{<br/>&nbsp;'%s':{'url':'%s','width':%s,'height':%s},<br />}"%(key,url,size.width,size.height))
upload.put()
else:
blobstore.delete(blob_keys=blob_key)
self.response.out.write("Not an image")
def get(self): self.post()
@app.route('/editmap')
def serve_map_editor_redirect():
return redirect(url_for('editmap/main'))
class PhotoUploader(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=gdi(self); user=get_user(self,domain)
if security_threat(self,domain): return
upload_url=blobstore.create_upload_url('/admin/upload/handler',gs_bucket_name=domain.gcs_bucket+"/files")
whtml(self,"utility/htmls/uploader.html",domain=domain,upload_url=upload_url)
# from /admin/executor set the user.info.map_editor flag to True for artists, this also unlocks the interface in the game menu
@app.route('/editmap/<name>',methods=['GET'])
def serve_editmap_get(name=""):
if not name: return "no map"
name=name.split("/")[0]
domain=gdi(request); user=get_user(request,domain)
if not user or not (gf(user,"map_editor") or getattr(user,"admin",False)): return make_response("Not Permitted!")
map=get_by_iid("map|%s"%name)
return whtml(request,"utility/htmls/map_editor.html",domain=domain,name=name,map=map,tilesets=tilesets,community=1)
class Items(webapp.RequestHandler): #NOTICE: << Publicly available at /communityitems! >> [10/10/16]
@ndb.toplevel
def get(self):
domain=gdi(self)
#if not is_current_user_admin():
# user=get_user(self,domain)
# if not user or user.k()+"" not in [ellian_id,hello_id]: return
mode,items=gdmul(self,"mode","items")
whtml(self,"utility/htmls/items.html",domain=domain,mode=mode,items=items)
def post(self): self.get()
@ndb.toplevel
@app.route('/editmap/<name>',methods=['POST'])
def serve_editmap_post(name=""):
data=request.values.get("data")
name=name.split("/")[0]
domain=gdi(request); user=get_user(request,domain)
if not user or not (gf(user,"map_editor") or getattr(user,"admin",False)): return make_response("Not Permitted!")
map=get_by_iid("map|%s"%name)
if not map: map=Map(key=ndb.Key(Map,name),info=GG())
map.info.data=json.loads(data)
process_map(map)
map.updated=datetime.now()
map.put()
copy_map(name,"test")
return make_response(to_pretty_num(olen(map.info.data)))
class Backups(webapp.RequestHandler):
@ndb.toplevel
def get(self,m,id):
domain=gdi(self)
backups=Backup.query(Backup.backup_item_id=="%s|%s"%(m,id)).order(-Backup.backup_created).fetch(50)
whtml(self,"utility/htmls/backups.html",domain=domain,backups=backups)
def post(self,m,id): self.get(m,id)
@app.route('/admin/selector')
@app.route('/communityselector')
def serve_selector_options():
return whtml(request,"utility/htmls/imagesets/select-imageset.html",domain=gdi(request),imagesets=imagesets)
application = webapp.WSGIApplication([
('/admin/renderer', Renderer),
('/admin/executor', Executor),
('/admin/items', Items),
('/admin/upload', PhotoUploader),
('/admin/upload/handler', PhotoUpload),
('/admin/backups/(.*)/(.*)', Backups),
('/admin/events/?(.*)', Events),
('/admin/map_editor/?(.*)', MapEditor),
('/admin/selector/?(.*)', Selector),
('/admin/items', Items),
],debug=is_sdk)
@app.route('/admin/selector/<name>')
@app.route('/communityselector/<name>') #NOTICE: << Publicly available at /communityselector! >> [10/10/16]
def serve_selector(name=""):
name=name.split("/")[0]
file=imagesets[name]["file"]
size=imagesets[name]["size"]
width=imagesets[name]["columns"]*size
height=imagesets[name]["rows"]*size
xs=[]; ys=[]
for i in xrange(width/size): xs.append(i)
for j in xrange(height/size): ys.append(j)
return whtml(request,"utility/htmls/imagesets/selector.html",domain=gdi(request),name=name,file=file,size=size,width=width,height=height,xs=xs,ys=ys,scale=3)
#TODO: Re-implement uploaders at uploaders.py
@app.route('/admin/items')
@app.route('/communityitems') #NOTICE: << Publicly available at /communityitems! >> [10/10/16]
def serve_items():
domain=gdi(request)
#if not is_current_user_admin():
# user=get_user(self,domain)
# if not user or user.k()+"" not in [ellian_id,hello_id]: return
mode,items=gdmul(request,"mode","items")
return whtml(request,"utility/htmls/items.html",domain=domain,mode=mode,items=items)
@app.route('/admin/backups/<m>/<id>')
@ndb.toplevel
def serve_backups(m,id):
domain=gdi(request); user=get_user(request,domain)
if not user or not getattr(user,"admin",False): return "no permission"
backups=Backup.query(Backup.backup_item_id=="%s|%s"%(m,id)).order(-Backup.backup_created).fetch(50)
whtml(request,"utility/htmls/backups.html",domain=domain,backups=backups)
@app.route('/admin/ips')
def serve_ips():
domain=gdi(request); user=get_user(request,domain)
if not user or not getattr(user,"admin",False): return "no permission"
def ip_to_html(ip):
return "<div style='margin-bottom: 2px'>%s {%s} [%s]</div>"%(ip.info.explanation,ip.random_id,ip.info.limit)
html="<style> html{background-color:gray}</style>"
for ip in IP.query(IP.exception==True).fetch(5000): html+=ip_to_html(ip)
return html
@app.route('/admin/visualize/<name>')
def serve_visualize(name=""):
domain=gdi(request); user=get_user(request,domain)
if not user or not getattr(user,"admin",False): return "no permission"
character=get_character(name)
if not character: return whtml(request,"htmls/simple_message.html",domain=domain,message="Not Found")
characters=[character]
for c in Backup.query(Backup.backup_item_id==character.k('i')).order(-Backup.backup_created).fetch(80):
c.name=c.info.name="%s%s"%(c.info.name,(datetime.now()-c.backup_created).days)
logging.info(c.info.slots["mainhand"]["name"])
characters.append(c)
domain.title=character.info.name
return whtml(request,"htmls/player.html",domain=domain,characters=characters)
+43 -44
View File
@@ -87,7 +87,7 @@ def signup_or_login_api(**args):
{"type":"success","message":"Signup Complete!" },
selection_info(self,user,domain),
])
add_event(user,"signup",["new","noteworthy"],self=self,info=cGG(message="Signup %s"%(user.info.email)),async=True)
add_event(user,"signup",["new","noteworthy"],self=self,info=cGG(message="Signup %s"%(user.info.email)),_async=True)
increase_signupth()
try:
@@ -503,7 +503,7 @@ def create_character_api(**args):
{"type":"success","message":"%s is alive!"%name },
selection_info(self,owner,domain),
])
add_event(owner,"new_character",["characters","noteworthy"],self=self,info=cGG(message="New Character %s from %s"%(character.info.name,user.info.email)),backup=True,async=True)
add_event(owner,"new_character",["characters","noteworthy"],self=self,info=cGG(message="New Character %s from %s"%(character.info.name,user.info.email)),backup=True,_async=True)
increase_characterth()
def sort_characters_api(**args):
@@ -846,8 +846,8 @@ def cli_time_api(**args):
if result:
logging.info(result)
return jhtmlm(self,"Purchased 7 more days of CLI time for %s shells! You have: %.2d"%(amount,-dsince(result.cli_time)))
if amount>0: add_event(result,"bill",["cashflow"],self=self,info=cGG(message="%s [%s] spent %s shells for: %s"%(name,user.k(),amount,reason),amount=amount,reason=reason),async=True)
else: add_event(result,"ishells",["cashflow"],self=self,info=cGG(message="%s [%s] received %s shells from: %s"%(name,user.k(),-amount,reason),amount=-amount,reason=reason),async=True)
if amount>0: add_event(result,"bill",["cashflow"],self=self,info=cGG(message="%s [%s] spent %s shells for: %s"%(name,user.k(),amount,reason),amount=amount,reason=reason),_async=True)
else: add_event(result,"ishells",["cashflow"],self=self,info=cGG(message="%s [%s] received %s shells from: %s"%(name,user.k(),-amount,reason),amount=-amount,reason=reason),_async=True)
try: deferred.defer(update_characters,result)
except: log_trace()
else:
@@ -870,8 +870,8 @@ def bill_user_api(**args):
if result:
logging.info(result)
jhtml(self,{"done":1,"cash":result.cash})
if amount>0: add_event(result,"bill",["cashflow"],self=self,info=cGG(message="%s [%s] spent %s shells for: %s"%(name,user.k(),amount,reason),amount=amount,reason=reason),async=True)
else: add_event(result,"ishells",["cashflow"],self=self,info=cGG(message="%s [%s] received %s shells from: %s"%(name,user.k(),-amount,reason),amount=-amount,reason=reason),async=True)
if amount>0: add_event(result,"bill",["cashflow"],self=self,info=cGG(message="%s [%s] spent %s shells for: %s"%(name,user.k(),amount,reason),amount=amount,reason=reason),_async=True)
else: add_event(result,"ishells",["cashflow"],self=self,info=cGG(message="%s [%s] received %s shells from: %s"%(name,user.k(),-amount,reason),amount=-amount,reason=reason),_async=True)
try: deferred.defer(update_characters,result)
except: log_trace()
else:
@@ -1021,7 +1021,7 @@ def send_mail_api(**args):
if fro and not frop or not top: return jhtml(self,{"failed":1,"reason":"nocharacter","return":True})
if fro: user1=get_by_iid_async("user|%s"%frop.owner);
user2=get_by_iid("user|%s"%top.owner)
ud2=get_user_data(user2,async=True); c2=Mail.query(Mail.owner==user2.k(),Mail.read==False).fetch_async(98,keys_only=True)
ud2=get_user_data(user2,_async=True); c2=Mail.query(Mail.owner==user2.k(),Mail.read==False).fetch_async(98,keys_only=True)
if fro: user1=user1.get_result()
if not server: return jhtml(self,{"failed":1,"reason":"noserver"})
if fro and not user1 or not user2: return jhtml(self,{"failed":1,"reason":"nouser","return":True})
@@ -1183,7 +1183,7 @@ def stripe_payment_api(**args): #TODO: Patch for .server checks [10/07/18]
currency="usd",
description="%s SHELLS for %s USD"%(shells,usd),
source=token)
add_event(user,"stripe",["payments","cashflow"],self=self,info=cGG(message="STRIPE! %s spent %s USD!"%(user.name,usd),usd=usd,token=token,response=response),async=True)
add_event(user,"stripe",["payments","cashflow"],self=self,info=cGG(message="STRIPE! %s spent %s USD!"%(user.name,usd),usd=usd,token=token,response=response),_async=True)
def stripe_transaction():
element=get_by_iid(user.k('i'))
element.cash+=int(shells)
@@ -1191,7 +1191,7 @@ def stripe_payment_api(**args): #TODO: Patch for .server checks [10/07/18]
return element
result=ndb.transaction(stripe_transaction,xg=True,retries=120)
if result:
add_event(result,"shells",["cashflow"],self=self,info=cGG(message="STRIPE! %s received %s SHELLS!"%(user.name,shells),usd=usd,token=token),async=True)
add_event(result,"shells",["cashflow"],self=self,info=cGG(message="STRIPE! %s received %s SHELLS!"%(user.name,shells),usd=usd,token=token),_async=True)
jfunc(self,"stripe_result",["success",result.cash])
jhtmls(self,"You received %s SHELLS!"%shells)
try: deferred.defer(add_cash,result.k(),int(shells),referrer=True,only_referrer=True)
@@ -1304,7 +1304,7 @@ def start_character_api(**args):
return jhtml(self,data)
if is_in_game(character): jhtml(self,{"failed":1,"reason":"ingame"}); return
if not is_sdk and ssince(gf(character,"last_start",really_old))<40: jhtml(self,{"failed":1,"reason":"wait_%d_seconds"%(40-ssince(gf(character,"last_start",really_old)))}); return
add_event(character,"start",["activity"],self=self,info=cGG(message="%s [LV.%s] logged into %s %s"%(character.info.name,character.level,server.region,server.name),server=server.k('i')),async=True)
add_event(character,"start",["activity"],self=self,info=cGG(message="%s [LV.%s] logged into %s %s"%(character.info.name,character.level,server.region,server.name),server=server.k('i')),_async=True)
if user.guild: guild=get_by_iid_async("guild|%s"%user.guild)
def start_character_transaction():
element=get_by_iid(character.k('i'))
@@ -1380,7 +1380,7 @@ def stop_character_api(**args):
if result:
character=result
jhtml(self,{"done":1,"name":character.info.name})
add_event(character,"stop",["activity"],self=self,info=cGG(message="%s [LV.%s] logged out of %s %s"%(character.info.name,character.level,server.region,server.name),server=server.k('i')),async=True)
add_event(character,"stop",["activity"],self=self,info=cGG(message="%s [LV.%s] logged out of %s %s"%(character.info.name,character.level,server.region,server.name),server=server.k('i')),_async=True)
else:
jhtml(self,{"failed":1,"reason":"unknown"})
@@ -1490,10 +1490,10 @@ def reload_server_api(**args):
def create_server_api(**args):
self,domain,keyword,port,region,pvp,gameplay,sname=gdmuld(args,"self","domain","keyword","port","region","pvp","gameplay","name")
if keyword!=secrets.SERVER_MASTER: jhtml(self,{"failed":1}); return
actual_ip=ip=self.request.remote_addr; server_name="XX"
actual_ip=ip=request.remote_addr; server_name="XX"
if is_sdk: actual_ip=ip=domain.server_ip
if domain.https_mode: ip="%s.%s"%(ip_to_subdomain.get(ip,ip),live_domain)
lat,lon=(self.request.headers.get("X-Appengine-Citylatlong")or"0,0").split(",")
lat,lon=(request.headers.get("X-Appengine-Citylatlong")or"0,0").split(",")
try: lat,lon=float(lat),float(lon)
except: lat,lon=0,0
@@ -1685,7 +1685,6 @@ def delete_map_api(**args):
jhtml(self,[{"type":"success","message":"Done!"},selection_info(self,user,domain)])
def test_api(**args):
asdfdfsa=dsfafsd
self,user,server,domain=gdmuld(args,"self","user","server","domain")
logging.info("test")
jfunc(self,"alert",["lol"])
@@ -1705,34 +1704,34 @@ def simplify_args_for_logging(args):
except:
return args
class APICall(webapp.RequestHandler):
@ndb.toplevel
def post(self):
domain=gdi(self)
server=get_server(self,domain)
args,method=gdmul(self,"arguments","method")
if security_threat(self,domain): return
user=get_user(self,domain,api_override=(server and method and method!="start_character")) # only "start_character" has an auth check, the rest doesn't re-check user auths #IMPORTANT! [09/08/20]
method="%s_api"%method
if args: args=json.loads(args)
if not args: args={}
logging.info("\n\nAPI Method Called: %s Arguments: %s\n"%(method,simplify_args_for_logging(args)))
#logging.info(self.request.cookies.get("auth"))
if not user and method not in ["log_error_api","signup_or_login_api","load_article_api","get_server_api","test_api","create_server_api","stop_server_api","update_server_api","reload_server_api","set_friends_api","not_friends_api","password_reminder_api","server_event_api","reset_password_api","ban_user_api","send_mail_api","log_chat_api","take_item_from_mail_api","broadcast_api"]:
if self.request.get("server_auth"): jhtml(self,{"failed":1,"reason":"nouser"}); return
jhtml(self,[{"type":"func","func":"add_log","args":["Not logged in."]}]); return
function=globals().get(method)
args["domain"]=domain
args["user"]=user
args["server"]=server
args["self"]=self
if user and not server and gf(user,"blocked_until",really_old)>datetime.now() and method not in ["logout_api_X"]: return jhtml(self,[{"type":"func","func":"add_log","args":["Blocked"]}])
if function: function(**args)
else:
if args.get("dataType")=="json": jhtml(self,[{"type":"ui_log","message":"Invalid method"}])
else: logging.error("no function")
safe_commit(user); safe_commit(server); domain_routine(domain)
@app.route('/api', methods=['POST'])
@app.route('/api/<name>', methods=['POST'])
@app.route('/api/<name>/<extra>', methods=['POST'])
@ndb.toplevel
def server_api(name="",extra=""):
self=request # [26/01/24] easier for now
domain=gdi(self)
server=get_server(self,domain)
args,method=gdmul(self,"arguments","method")
if security_threat(self,domain): return "security_threat"
user=get_user(self,domain,api_override=(server and method and method!="start_character")) # only "start_character" has an auth check, the rest doesn't re-check user auths #IMPORTANT! [09/08/20]
method="%s_api"%method
if args: args=json.loads(args)
if not args: args={}
logging.info("\n\nAPI Method Called: %s Arguments: %s\n"%(method,simplify_args_for_logging(args)))
if not user and method not in ["log_error_api","signup_or_login_api","load_article_api","get_server_api","test_api","create_server_api","stop_server_api","update_server_api","reload_server_api","set_friends_api","not_friends_api","password_reminder_api","server_event_api","reset_password_api","ban_user_api","send_mail_api","log_chat_api","take_item_from_mail_api","broadcast_api"]:
if self.values.get("server_auth"): jhtml(self,{"failed":1,"reason":"nouser"}); return "nouser"
jhtml(self,[{"type":"func","func":"add_log","args":["Not logged in."]}]); return "notloggedin"
function=globals().get(method)
args["domain"]=domain
args["user"]=user
args["server"]=server
args["self"]=self
if user and not server and gf(user,"blocked_until",really_old)>datetime.now() and method not in ["logout_api_X"]: return jhtml(self,[{"type":"func","func":"add_log","args":["Blocked"]}])
if function: function(**args)
else:
if args.get("dataType")=="json": jhtml(self,[{"type":"ui_log","message":"Invalid method"}])
else: logging.error("no function")
safe_commit(user); safe_commit(server); domain_routine(domain)
return request.response
application = webapp.WSGIApplication([
('/api.*',APICall),
],debug=is_sdk)
+13 -13
View File
@@ -36,20 +36,20 @@ handlers:
static_dir: sounds
- url: /js
static_dir: js
- url: /api.*
script: api.application
- url: /admin.*
script: admin.application
login: admin
- url: /cr/.*
script: crons.application
login: admin
- url: /tasks.*
script: tasks.application
- url: /test.*
script: tests.application
- url: .*
script: main.application
script: main.app
- url: /admin/upload.*
script: uploaders.application
- url: /upload.*
script: uploaders.application
- url: /tileset/.*
script: uploaders.application
# skip_files:
# - ^lib(/.*)?
# - .*lib.*
# - ^electron(/.*)?
# - .*electron.*
libraries:
- name: jinja2
+75 -46
View File
@@ -3,26 +3,50 @@ from __future__ import with_statement
# Python3 compatibility [06/01/24]
from __future__ import print_function
import os,logging,cgi
if os.environ.get('SERVER_SOFTWARE', '').startswith('Dev'):
if os.environ.get('SERVER_SOFTWARE', '').startswith('Dev') or not os.getenv('GAE_ENV', '').startswith('standard'):
try:
from google.appengine.tools.dev_appserver import HardenedModulesHook
HardenedModulesHook._WHITE_LIST_C_MODULES += ['_ctypes', 'gestalt']
except: pass
import sys,json,random,time,re,math,copy,base64,cPickle,jinja2,types,urllib,operator,pickle,unicodedata
import sys,json,random,time,re,math,copy,base64,jinja2,types,urllib,operator,pickle,unicodedata
import gc as gbc
from lxml import etree as lxmletree
import datetime as rdatetime
import time as rtime
from datetime import datetime,timedelta,date
from urlparse import urlparse
from google.appengine.api.users import is_current_user_admin
from google.appengine.ext.webapp import blobstore_handlers # template,util,
from google.appengine.api import memcache,urlfetch,urlfetch_errors,mail,taskqueue,images,files,namespace_manager,search,modules,logservice
from google.appengine.ext import webapp,ndb,blobstore,deferred
if 1/2 == 0: #python2
from urlparse import urlparse
urlencode=urllib.urlencode
# webapp/blobstore_handlers is for photo upload and resort tileset upload - so that the python2 sdk still retains upload capabilities [27/01/24]
from google.appengine.ext import webapp,vendor
from google.appengine.ext.webapp import blobstore_handlers
from google.appengine.api import files,logservice # I don't think these were originally used
vendor.add('lib')
from libraries import stripe
from libraries import amazon_ses
import cPickle
else: #python3
basestring=str
xrange=range
from urllib.parse import urlparse
urlencode= urllib.parse.urlencode
from google.appengine.api import wrap_wsgi_app
from libraries import stripe3 as stripe
from libraries import amazon_ses3 as amazon_ses
import _pickle as cPickle
from google.appengine.api import memcache,urlfetch,urlfetch_errors,mail,taskqueue,images,namespace_manager,search,modules
from google.appengine.ext import ndb,blobstore,deferred
from google.appengine.runtime import DeadlineExceededError
from google.appengine.runtime.apiproxy_errors import DeadlineExceededError as DeadlineExceededError2
from google.appengine.datastore.datastore_query import Cursor
from flask import Flask, render_template, request, make_response
app = Flask(__name__)
if 1/2 != 0:
app.wsgi_app = wrap_wsgi_app(app.wsgi_app, use_deferred=True)
class mj2u(jinja2.Undefined):
def __str__(self): 0 and logging.info("Non existing variable"); return ""
def __call__(self,*a,**d): 0 and logging.info("Non existing variables non existing call with a %s d %s"%(a,d)); return "";mj2u()
@@ -34,11 +58,9 @@ class GG(): pass
import secrets
from libraries.country_to_latlon import c_to_ll
from libraries import stripe
from libraries import amazon_ses
#from libraries import get_image_size
if os.environ.get('SERVER_SOFTWARE', '').startswith('Dev'):
if os.environ.get('SERVER_SOFTWARE', '').startswith('Dev') or not os.getenv('GAE_ENV', '').startswith('standard'):
is_sdk=True; is_production=is_appengine=False
stripe.verify_ssl_certs = False
stripe.api_key=secrets.stripe_test_api_key
@@ -82,12 +104,12 @@ if is_production:
#maps["desertland"]["key"]="jayson_desertland_copy"
pass
game_version=785
game_version=786
SALES=4+5+388+5101+125/20 #donation+manual+macos+steam+sales
update_notes=[
"Lunar New Year Event",
"Last Update [19th of January]",
"Adding Python3 compatibility",
"Switched to Python3",
]
ip_to_subdomain={ #IMPORTANT: SPECIAL PAGE RULES ARE NEEDED: https://dash.cloudflare.com/b6f5a13bded5fdd273e4a1cd3777162d/adventure.land/page-rules - uss1 / eus1 was best
"35.187.255.184":"asia1",
@@ -110,11 +132,18 @@ live_domain='adventure.land'
sdk_domain='thegame.com'
SDK_UPLOAD_PASSWORD=ELEMENT_PASSWORD=secrets.sdk_password
def gdi(self=None):
def init_request(request):
if request and not getattr(request,"response",None):
request.response=make_response("")
# this way cookies etc. can be set from the request object ahead of time
# otherwise the Flask flow is limiting as it doesn't let you do these things from function calls ahead of response creation [25/01/24]
def gdi(request=None):
init_request(request)
domain=GG()
if is_sdk:
if self:
url=urlparse(self.request.url)
if request:
url=urlparse(request.url)
protocol=url.scheme
hostname=url.hostname
else:
@@ -130,7 +159,7 @@ def gdi(self=None):
domain.domain=hostname
else:
protocol="http"
if self and "https" in (self.request.headers.get("Cf-Visitor") or ""): protocol="https"
if request and "https" in (request.headers.get("Cf-Visitor") or ""): protocol="https"
domain.base_url=protocol + "://" + live_domain
domain.pref_url=domain.base_url
domain.stripe_pkey=stripe_pkey
@@ -186,56 +215,56 @@ def gdi(self=None):
domain.access_master=secrets.ACCESS_MASTER
domain.servers=[]
domain.characters=[]
try: domain.scale=int(float(self and self.request.get("scale") or 2))
try: domain.scale=int(float(request and request.values.get("scale") or 2))
except: domain.scale=2
try: domain.times=int(self and self.request.get("times") or 0)
try: domain.times=int(request and request.values.get("times") or 0)
except: domain.times=0
domain.load_character=self and self.request.get("load") or ""
domain.load_character=request and request.values.get("load") or ""
domain.electron=False
domain.platform="web"
domain.update_notes=update_notes
if self:
if "Electron" in self.request.headers.get("User-Agent",""):
if request:
if "Electron" in request.headers.get("User-Agent",""):
domain.electron=True
if self.request.get("buildid") and "win32" in self.request.get("buildid"): domain.platform="steam"
if self.request.get("buildid") and "darwin" in self.request.get("buildid"): domain.platform="mac"
if self.request.cookies.get("music")!="off": domain.music_on=True
if self.request.cookies.get("sfx")!="off": domain.sfx_on=True
domain.url=self.request.url
domain.section=self.request.get("section")
if self.request.get("test_clicks"): domain.test_clicks=True; domain.pixi_version="4.0.2"
if self.request.get("no_cache"): domain.v=100000+random.randrange(0,100000000)
if self.request.get("no_html"): domain.no_html=True
if self.request.get("no_html")=="bot": domain.no_html="bot"
if self.request.get("is_bot"): domain.is_bot=True
if self.request.get("is_cli"): domain.is_cli=True
if self.request.get("recording_mode"): domain.recording_mode=True; domain.boost=20; domain.sfx_on=False; domain.music_on=False
if self.request.get("no_graphics") or domain.no_html:
if request.values.get("buildid") and "win32" in request.values.get("buildid"): domain.platform="steam"
if request.values.get("buildid") and "darwin" in request.values.get("buildid"): domain.platform="mac"
if request.cookies.get("music")!="off": domain.music_on=True
if request.cookies.get("sfx")!="off": domain.sfx_on=True
domain.url=request.url
domain.section=request.values.get("section")
if request.values.get("test_clicks"): domain.test_clicks=True; domain.pixi_version="4.0.2"
if request.values.get("no_cache"): domain.v=100000+random.randrange(0,100000000)
if request.values.get("no_html"): domain.no_html=True
if request.values.get("no_html")=="bot": domain.no_html="bot"
if request.values.get("is_bot"): domain.is_bot=True
if request.values.get("is_cli"): domain.is_cli=True
if request.values.get("recording_mode"): domain.recording_mode=True; domain.boost=20; domain.sfx_on=False; domain.music_on=False
if request.values.get("no_graphics") or domain.no_html:
domain.no_graphics=True;
domain.pixi_version="fake" #NOGTODO is the hashtag
domain.pixi_fversion="?v=%s"%game_version
if self.request.get("borders") or self.request.get("border_mode"): domain.border_mode=True
if self.request.cookies.get("perfect_pixels_off") and not SCREENSHOT_MODE:
if request.values.get("borders") or request.values.get("border_mode"): domain.border_mode=True
if request.cookies.get("perfect_pixels_off") and not SCREENSHOT_MODE:
domain.perfect_pixels=False
if self.request.cookies.get("d_lines_off"):
if request.cookies.get("d_lines_off"):
domain.d_lines=False
if self.request.cookies.get("no_tutorial"):
if request.cookies.get("no_tutorial"):
domain.tutorial=False
if self.request.cookies.get("no_fast_mode"):
if request.cookies.get("no_fast_mode"):
domain.fast_mode=False
if self.request.get("engine") or self.request.cookies.get("engine_mode"):
domain.engine_mode=self.request.get("engine") or self.request.cookies.get("engine_mode")
if self.request.cookies.get("sd_lines_off"):
if request.values.get("engine") or request.cookies.get("engine_mode"):
domain.engine_mode=request.values.get("engine") or request.cookies.get("engine_mode")
if request.cookies.get("sd_lines_off"):
domain.sd_lines=False
if self.request.cookies.get("pro_mode"):
if request.cookies.get("pro_mode"):
domain.newcomer_ui=False
if self.request.cookies.get("no_weapons"):
if request.cookies.get("no_weapons"):
domain.new_attacks=False
if self.request.cookies.get("manual_reload"):
if request.cookies.get("manual_reload"):
domain.auto_reload="off"
else:
domain.auto_reload="auto"
if self.request.scheme=="https" or self.request.headers.get("Cf-Visitor") and self.request.headers.get("Cf-Visitor").find("https")!=-1:
if request.scheme=="https" or request.headers.get("Cf-Visitor") and request.headers.get("Cf-Visitor").find("https")!=-1:
domain.https=True
return domain
secure_cookies=False #not is_sdk
+75 -70
View File
@@ -164,6 +164,10 @@ def all_user_cron(num): #<<NEW
user.banned=True
user.put()
def all_user_cron(num): #<<NEW
for user in User.query(User.random_number==num):
logging.info(user.info.email)
def all_character_cron(num):
for character in Character.query(Character.random_number==num):
if gf(character,"map") in ["main","batcave"]:
@@ -214,7 +218,7 @@ def all_cbackup_cron(num):
element=get_by_iid(character.k('i'))
element.to_backup=False
element.put()
rpcs.append(backup_item(character,async=True))
rpcs.append(backup_item(character,_async=True))
rpcs.append(ndb.transaction_async(backup_transaction,retries=0))
for rpc in rpcs: rpc.wait()
@@ -225,7 +229,7 @@ def all_process_cron(num):
element=get_by_iid(character.k('i'))
element.to_backup=False
element.put()
rpcs.append(backup_item(character,async=True))
rpcs.append(backup_item(character,_async=True))
rpcs.append(ndb.transaction_async(backup_transaction,retries=0))
for rpc in rpcs: rpc.wait()
@@ -236,79 +240,80 @@ def all_ubackup_cron(num):
element=get_by_iid(user.k('i'))
element.to_backup=False
element.put()
rpcs.append(backup_item(user,async=True))
rpcs.append(backup_item(user,_async=True))
rpcs.append(ndb.transaction_async(backup_transaction,retries=0))
for rpc in rpcs: rpc.wait()
class AllCron(webapp.RequestHandler):
@ndb.toplevel
def get(self,mname):
if mname=="user":
for n in range(1,101):
deferred.defer(all_user_cron,n,_queue="important")
if mname=="mail":
for n in range(1,101):
deferred.defer(all_mail_cron,n,_queue="important")
if mname=="character":
for n in range(1,101):
deferred.defer(all_character_cron,n,_queue="important")
if mname=="backups":
#originally, backups were on-access, but they need to be all at the same time, so a reversal restores the ~whole state of the game [19/11/18]
for n in range(1,101):
deferred.defer(all_ubackup_cron,n,_queue="important")
deferred.defer(all_cbackup_cron,n,_queue="important")
if mname=="process_backups":
for n in range(1,101):
deferred.defer(all_process_cron,n,_queue="important")
def post(self,mname): self.get(mname)
@app.route('/cr/all/<mname>',methods=['GET','POST'])
@ndb.toplevel
def serve_all_cron(mname=""):
domain=gdi(request); user=get_user(request,domain)
if not (user and getattr(user,"admin",False) or request.headers.get('X-AppEngine-Cron')): return "no permission"
if mname=="user":
for n in range(1,101):
deferred.defer(all_user_cron,n,_queue="important")
if mname=="mail":
for n in range(1,101):
deferred.defer(all_mail_cron,n,_queue="important")
if mname=="character":
for n in range(1,101):
deferred.defer(all_character_cron,n,_queue="important")
if mname=="backups":
#originally, backups were on-access, but they need to be all at the same time, so a reversal restores the ~whole state of the game [19/11/18]
for n in range(1,101):
deferred.defer(all_ubackup_cron,n,_queue="important")
deferred.defer(all_cbackup_cron,n,_queue="important")
if mname=="process_backups":
for n in range(1,101):
deferred.defer(all_process_cron,n,_queue="important")
return ""
class CheckServers(webapp.RequestHandler):
@ndb.toplevel
def get(self): check_servers()
def post(self): self.get()
@app.route('/cr/check_servers',methods=['GET','POST'])
@ndb.toplevel
def serve_server_checker():
domain=gdi(request); user=get_user(request,domain)
if not (user and getattr(user,"admin",False) or request.headers.get('X-AppEngine-Cron')): return "no permission"
check_servers()
return ""
class UnstuckPlayers(webapp.RequestHandler):
@ndb.toplevel
def get(self):
servers=get_servers()
for server in servers:
if msince(server.created)>10: #no widespread network issue
characters=Character.query(Character.online==True, Character.server == server.k(), Character.last_sync < datetime.now()-timedelta(minutes=30)).fetch()
for character in characters:
m=msince(character.last_sync)
character.online=False
character.server=""
character.put()
send_email(gdi(),"kaansoral@gmail.com",html="Stuck for %s minutes"%m,title="UNSTUCK %s from %s"%(character.name,server.k()))
@app.route('/cr/unstuck',methods=['GET','POST'])
@ndb.toplevel
def serve_unstuck():
domain=gdi(request); user=get_user(request,domain)
if not (user and getattr(user,"admin",False) or request.headers.get('X-AppEngine-Cron')): return "no permission"
servers=get_servers()
for server in servers:
if msince(server.created)>10: #no widespread network issue
characters=Character.query(Character.online==True, Character.server == server.k(), Character.last_sync < datetime.now()-timedelta(minutes=30)).fetch()
for character in characters:
m=msince(character.last_sync)
character.online=False
character.server=""
character.put()
send_email(gdi(),"kaansoral@gmail.com",html="Stuck for %s minutes"%m,title="UNSTUCK %s from %s"%(character.name,server.k()))
return ""
def post(self): self.get()
class Hourly(webapp.RequestHandler):
@ndb.toplevel
def get(self):
deferred.defer(verify_steam_installs)
return
@app.route('/cr/hourly',methods=['GET','POST'])
@ndb.toplevel
def serve_hourly():
domain=gdi(request); user=get_user(request,domain)
if not (user and getattr(user,"admin",False) or request.headers.get('X-AppEngine-Cron')): return "no permission"
deferred.defer(verify_steam_installs)
return ""
if random.randrange(0,20)==2:
user=get_by_iid("user|%s"%get_character("Wizard").owner)
if random.randrange(0,20)==2:
user=get_by_iid("user|%s"%get_character("Wizard").owner)
if random.randrange(0,20)==2:
add_cash(user,100*random.randrange(1,50))
else:
add_cash(user,200)
add_cash(user,100*random.randrange(1,50))
else:
add_cash(user,200)
if random.randrange(0,20)==2:
user=get_by_iid("user|%s"%get_character("Oragon").owner)
if random.randrange(0,20)==2:
user=get_by_iid("user|%s"%get_character("Oragon").owner)
if random.randrange(0,20)==2:
add_cash(user,100*random.randrange(1,50))
else:
add_cash(user,200)
#user=get_by_iid("user|%s"%get_character("Trexnamedtod").owner)
#add_cash(user,25)
#user=get_by_iid("user|%s"%get_character("Emerald").owner)
#add_cash(user,5)
def post(self): self.get()
application = webapp.WSGIApplication([
('/cr/check_servers', CheckServers),
('/cr/hourly', Hourly),
('/cr/unstuck', UnstuckPlayers),
('/cr/all/([^/]*)/?', AllCron),
],debug=is_sdk)
add_cash(user,100*random.randrange(1,50))
else:
add_cash(user,200)
#user=get_by_iid("user|%s"%get_character("Trexnamedtod").owner)
#add_cash(user,25)
#user=get_by_iid("user|%s"%get_character("Emerald").owner)
#add_cash(user,5)
return ""
+5 -2
View File
@@ -538,7 +538,10 @@ animations={
},
}
from projectiles import projectiles
try:
from projectiles import projectiles
except:
from design.projectiles import projectiles
for id in projectiles:
if projectiles[id].has_key("animation") and animations[projectiles[id]["animation"]].has_key("speed"):
if "animation" in projectiles[id] and "speed" in animations[projectiles[id]["animation"]]:
animations[projectiles[id]["animation"]]["speed"]=projectiles[id]["speed"]
+7 -4
View File
@@ -1,5 +1,8 @@
import copy
from items import items
try:
from items import items
except:
from design.items import items
drops={
"gold":{
@@ -1222,7 +1225,7 @@ drops={
}
for n,nm in [[1,25],[2,25],[3,25],[4,25],[5,22]]:
for num in xrange(nm):
for num in range(nm):
if "%s%.2d"%(n,num) in ["122","203","404"]: continue
drops["cosmo2"].append([1,"cx","hairdo%s%.2d"%(n,num)])
@@ -1231,7 +1234,7 @@ drops["cosmo2"].append([1.0/3,"cx","hairdo404"])
drops["cosmo2"].append([1.0/10,"cx","hairdo203"])
for n,nm in [[1,16],[2,25],[3,24],[4,8]]:
for num in xrange(nm):
for num in range(nm):
if "%s%.2d"%(n,num) in ["100","114","115","102","106","113","310","309","318","400","405"]: continue
drops["cosmo3"].append([1,"cx","hat%s%.2d"%(n,num)])
@@ -1264,7 +1267,7 @@ for name in items:
drops["glitch"].append([1,name])
for name in items:
if not items[name].has_key("g"):
if "g" not in items[name]:
import logging
logging.error(name)
if not items[name].get("ignore") and not items[name].get("exclusive"):
+8 -8
View File
@@ -1739,11 +1739,11 @@ for name in armor.keys():
res_gains=[0,0.5,2.5,5.5,7.5]
xtier=([0.5,0.5,2,3.4,4.5,6][ftier]+[0.5,0.5,2,4,5,6][ctier]+tier)/3.0
#if not current.has_key("edge") and tier>1: current["edge"]=(tier-1)*2
if tier<=1.25 and not current.has_key("grades"): current["grades"]=[7,9]
elif tier<=1.5 and not current.has_key("grades"): current["grades"]=[4,8]
elif tier<=2.4 and not current.has_key("grades"): current["grades"]=[0,7]
elif tier<=2.75 and not current.has_key("grades"): current["grades"]=[0,5]
elif not current.has_key("grades"): current["grades"]=[0,0]
if tier<=1.25 and "grades" not in current: current["grades"]=[7,9]
elif tier<=1.5 and "grades" not in current: current["grades"]=[4,8]
elif tier<=2.4 and "grades" not in current: current["grades"]=[0,7]
elif tier<=2.75 and "grades" not in current: current["grades"]=[0,5]
elif "grades" not in current: current["grades"]=[0,0]
if len(current["grades"])==2:
current["grades"].append(10); current["grades"].append(12)
if tier>=4:
@@ -2583,7 +2583,7 @@ accessories={
for name in accessories.keys():
current=accessories[name]
if not current.has_key("grades"): current["grades"]=[3,5]
if "grades" not in current: current["grades"]=[3,5]
#Ring of Mage PVP: 30 Armor, 10 Res. Piercing, 120HP
#Ring of Warrior PVP: 20 Res, 10 Armor, 10 Armor. Piercing
@@ -3839,8 +3839,8 @@ weapons={
for name in weapons.keys():
current=weapons[name]; tier=current.get("tier",1)
if not current.has_key("upgrade"): continue
if not current.has_key("grades"):
if "upgrade" not in current: continue
if "grades" not in current:
if tier<1: current["grades"]=[8,9]
elif tier<=1: current["grades"]=[7,9]
elif tier<=1.5: current["grades"]=[5,8]
+3 -3
View File
@@ -3,7 +3,7 @@
# if 1:
# if 1:
# if 1:
# for i in xrange(200):
# for i in range(200):
# new_xp=xp
# while new_xp>=100: new_xp/=10
# new_xp=int(new_xp)
@@ -25,7 +25,7 @@
# if 1:
# if 1:
# if 1:
# for i in xrange(200):
# for i in range(200):
# new_xp=xp
# while new_xp>=100: new_xp/=10
# new_xp=int(new_xp)
@@ -46,7 +46,7 @@
# if 1:
# if 1:
# if 1:
# for i in xrange(200):
# for i in range(200):
# new_xp=xp
# while new_xp>=100: new_xp/=10
# new_xp=int(new_xp)
+1 -1
View File
@@ -1169,7 +1169,7 @@ skills={
}
}
for name in skills:
if skills[name].has_key("condition") and not skills[name].has_key("duration") and conditions.has_key(skills[name]["condition"]) and conditions[skills[name]["condition"]]:
if "condition" in skills[name] and "duration" not in skills[name] and skills[name]["condition"] in conditions and conditions[skills[name]["condition"]]:
try:
skills[name]["duration"]=conditions[skills[name]["condition"]]["duration"]
except:
+7 -7
View File
@@ -1016,8 +1016,8 @@ for s in bodysets:
}
last=0
nmap={0:"a",1:"b",2:"c",3:"d",4:"e",5:"f",6:"g",7:"h"}
for i in xrange(2):
for j in xrange(4):
for i in range(2):
for j in range(4):
if last<s[3]:
sprites[s[0]]["matrix"][i][j]="%s%s"%(s[0],nmap[last])
last+=1
@@ -1062,7 +1062,7 @@ for s in rowsets:
"columns":s[2],
"matrix":[[None]*s[2]],
}
for i in xrange(s[2]):
for i in range(s[2]):
name="%s%.2d"%(s[0],i)
if s[1]=="hat": name=name.replace("hats","hat")
if i<s[3]:
@@ -1070,16 +1070,16 @@ for s in rowsets:
else:
sprites[s[0]]["matrix"][0][i]=None
for i in xrange(42):
for i in range(42):
sprites["makeup1"]["matrix"][0].append("makeup1%.2d"%i)
#for i in xrange(25):
#for i in range(25):
# sprites["hairdo2"]["matrix"][0].append("hair2%.2d"%i)
# for i in xrange(41):
# for i in range(41):
# sprites["hairs2"]["matrix"][0].append("hair2%.2d"%i)
# for i in xrange(41):
# for i in range(41):
# sprites["hairs3"]["matrix"][0].append("hair3%.2d"%i)
imagesets={
+145 -100
View File
@@ -218,7 +218,7 @@ def process_map(map):
if placement[1]+width>max_x: max_x=placement[1]+width
if placement[2]+height>max_y: max_y=placement[2]+height
for tile in data.get("tiles",[]):
if not marked.has_key(current):
if current not in marked:
logging.info("%s is unused"%current)
else:
marked[current]=last
@@ -259,8 +259,7 @@ def render_selection(self,user,domain,level=80,server=None):
if domain.is_cli and (not user or not user.cli_time or user.cli_time<datetime.now()) and level>=70:
domain.is_cli=False; domain.harakiri=True
start=datetime.now(); logging.info("User data in: %s"%ssince(start))
#if domain.no_html=="bot" and self.request.get("auth"): set_cookie(self,"auth",self.request.get("auth"))
whtml(self,"htmls/index.html",domain=domain,user=user,user_data=data,server=server,servers=servers,total=total,characters=characters)
return whtml(self,"htmls/index.html",domain=domain,user=user,user_data=data,server=server,servers=servers,total=total,characters=characters)
def render_comm(self,user,domain):
servers=get_servers(); server=select_server(self,user,servers); total=0; characters=[]; data={}
@@ -270,19 +269,19 @@ def render_comm(self,user,domain):
characters=get_characters(user)
domain.characters=characters_to_client(characters)
data=get_user_data(user);
whtml(self,"htmls/comm.html",domain=domain,user=user,user_data=data,server=server,servers=servers,total=total,characters=characters)
return whtml(self,"htmls/comm.html",domain=domain,user=user,user_data=data,server=server,servers=servers,total=total,characters=characters)
def selection_info(self,user,domain):
servers=get_servers(); server=select_server(self,user,servers)
return {"type":"content","html":shtml("htmls/contents/selection.html",user=user,domain=domain,server=server,servers=servers,characters=get_characters(user))}
def security_threat(self,domain):
referer=self.request.headers.get('Referer') or self.request.headers.get('Origin') or ""
def security_threat(request,domain):
referer=request.headers.get('Referer') or request.headers.get('Origin') or ""
if not referer: return False
referer=urlparse(referer).hostname
if not referer.endswith(domain.domain):
self.response.out.write("Threat detected")
request.response.set_data("Threat detected")
return True
# if is_production and domain.cf_always_on and not is_cloudflare(self):
@@ -299,7 +298,7 @@ def disable_email(email,logging=None):
user.put()
logging.info("Disabled emails for %s"%user)
def get_user(self=None,domain=None,email=None,phrase_check=False,api_override=None):
def get_user(request=None,domain=None,email=None,phrase_check=False,api_override=None):
if email:
try:
email=purify_email(email)
@@ -311,11 +310,11 @@ def get_user(self=None,domain=None,email=None,phrase_check=False,api_override=No
return User.query(User.email == email).get()
except:
logTrace()
elif self:
auth=get_cookie(self,"auth") or self.request.get("auth")
elif request:
auth=get_cookie(request,"auth") or request.values.get("auth")
if auth:
id,auth=auth.replace('"',"").split("-")
get_user_data(id,async=True) #trigger the get
get_user_data(id,_async=True) #trigger the get
user=get_by_iid("user|%s"%id)
if user and (api_override or auth in gf(user,"auths",[])):
return user
@@ -357,9 +356,9 @@ def ip_in_range(ip,net):
log_trace()
return False
def get_ip(self):
ip=self.request.remote_addr
cloudflare_ip=self.request.headers.get("CF-Connecting-IP")
def get_ip(request):
ip=request.remote_addr
cloudflare_ip=request.headers.get("CF-Connecting-IP")
cloudflare_ranges=[
'103.21.244.0/22',
'103.22.200.0/22',
@@ -389,10 +388,10 @@ def get_ip(self):
if ip.find(":")==-1 and ip_in_range(ip,crange): return cloudflare_ip
for crange in cloudflare_ranges_v6:
if ip.find(":")!=-1 and ip.startswith(crange[:7]): return cloudflare_ip #not sure 9 or 7 [25/08/13]
return self.request.remote_addr
return request.remote_addr
def get_country(self):
return self.request.headers.get("Cf-Ipcountry") or self.request.headers.get("X-Appengine-Country") or "XX"
def get_country(request):
return request.headers.get("Cf-Ipcountry") or request.headers.get("X-Appengine-Country") or "XX"
def get_character(id,phrase_check=False):
if id.isdigit() and get_by_iid("character|%s"%id): return get_by_iid("character|%s"%id)
@@ -417,9 +416,9 @@ def process_user_data(user,data):
calculate_tutorial_step(data)
return data
def get_user_data(user,async=False):
def get_user_data(user,_async=False):
if hasattr(user,"k"): user=user.k()
if async:
if _async:
return get_by_iid_async("infoelement|userdata-%s"%user)
else:
data=get_by_iid("infoelement|userdata-%s"%user)
@@ -431,7 +430,7 @@ def delete_user_data(user):
ndb.Key(InfoElement,"userdata-%s"%user).delete()
def reset_tutorial(user):
data=get_user_data(user,async=False)
data=get_user_data(user,_async=False)
data.info.completed_tasks=[] #completed tutorial tasks
data.info.tutorial_step=0 #tutorial step
data.put()
@@ -455,7 +454,7 @@ def remove_ip_exception(ip_a):
ip.put()
def get_ip_info(ip):
if type(ip) not in types.StringTypes: ip=get_ip(ip)
if not is_string(ip): ip=get_ip(ip)
info=get_by_iid("ip|%s"%ip)
if not info: info=IP(key=ndb.Key(IP,ip),info=cGG(users=[],characters=[],metrics={},last_decay=datetime.now()))
decay_ip_info(info)
@@ -493,11 +492,11 @@ def servers_to_client(domain,servers_data):
servers.append({"name":server.name,"region":server.region,"players":server.info.players,"key":server.k(),"addr":domain.https and server.ip or server.actual_ip,"port":server.port})
return servers
def get_characters(user,async=False):
def get_characters(user,_async=False):
characters=[]
for c in user.info.characters:
characters.append(get_by_iid_async("character|%s"%c["id"]))
if async: return characters
if _async: return characters
for i in xrange(len(characters)):
characters[i]=characters[i].get_result()
return characters
@@ -526,7 +525,7 @@ def character_to_dict(character):
data["map"]=character.info.map
data["x"]=character.info.x
data["y"]=character.info.y
if gf(character,"p") and character.info.p.has_key("home"):
if gf(character,"p") and "home" in character.info.p:
data["home"]=character.info.p["home"]
return data
@@ -628,7 +627,7 @@ def update_pids(character,data,owner):
def character_to_info(character,user=None,ip=None,guild=None):
drm=False
drm_fail=False
if user and user.created>datetime(2019,02,01,0,0) and not gf(user,"legacy_override"): drm=True
if user and user.created>datetime(2019,2,1,0,0) and not gf(user,"legacy_override"): drm=True
if gf(user,"drm_fail_pid","not")==character.pid: drm_fail=True
info={
"id":character.k(),
@@ -740,13 +739,13 @@ def pretty_timeleft(t):
left=left+" %d minutes"%minutes
return left
def gdpr_delete(self,user,logging=None):
def gdpr_delete(request,user,logging=None):
from api import delete_character_api
if not logging: logging=globals()["logging"]
if not hasattr(user,"k"): user=get_user(email=user)
for c in gf(user,"characters",[]):
user.info.last_delete=really_old
delete_character_api(user=user,self=self,domain=gdi(self),name=c["name"])
delete_character_api(user=user,self=request,domain=gdi(request),name=c["name"])
logging.info("Deleted %s"%c["name"])
markedphrase=get_by_iid("markedphrase|%s"%dgt("email",user.info.email))
if markedphrase: markedphrase.key.delete()
@@ -902,12 +901,12 @@ def character_analysis(name,logging=None):
logging.info("%s[%s] %s"%(character.info.name,character.level,to_pretty_num(character.info.gold)))
for h in gf(character,"p",{}).get("history",[]):
if h.get("from") and not names.has_key(h.get("from")):
if h.get("from") and h.get("from") not in names:
e=froms.get(h["from"],[0,0])
e[0]+=1
e[1]+=h.get("amount",0) or h.get("item","") and item_value({"name":h["item"],"level":h.get("level",0)}) or 0
froms[h["from"]]=e
if h.get("to") and not names.has_key(h.get("to")):
if h.get("to") and h.get("to") not in names:
e=tos.get(h["to"],[0,0])
e[0]+=1
e[1]+=h.get("amount",0) or h.get("item","") and item_value({"name":h["item"],"level":h.get("level",0)}) or 0
@@ -1180,7 +1179,7 @@ def select_server(self,user,servers):
min_dist=99999999999; the_server=None; max_rank=-99999; u_server=""
if user and len(gf(user,"characters",[])):
for c in gf(user,"characters",[]):
if c.has_key("home"):
if "home" in c:
u_server=c["home"]
break;
logging.info(u_server)
@@ -1282,7 +1281,7 @@ def refactor_item(current):
current["level"]=0
if current and items[current["name"]].get("s",""):
current["q"]=current.get("quantity",current.get("q",1))
if current.has_key("quantity"): del current["quantity"]
if "quantity" in current: del current["quantity"]
def refactor_character_items(character):
for i in range(0,len(character.info.items)):
@@ -1314,23 +1313,29 @@ def download_map(name,sdk_name=""):
def download_maps():
if not is_sdk: return
for m in maps:
element=download_by_iid("map|%s"%m)
if element: Map(key=ndb.Key(Map,m),info=element.info).put()
element=download_by_iid("map|%s"%maps[m]["key"])
if element: Map(key=ndb.Key(Map,maps[m]["key"]),info=element.info).put()
def empty_fill_maps():
if not is_sdk: return
for m in maps:
element=get_by_iid("map|%s"%maps[m]["key"])
if not element: Map(key=ndb.Key(Map,maps[m]["key"]),info=cGG(data={"tiles":[],"placements":[]})).put()
def copy_map(fr,to):
fr=get_by_iid("map|%s"%fr)
m=Map(key=ndb.Key(Map,to),info=fr.info); m.put()
def get_server(self,domain):
auth=self.request.get("server_auth")
def get_server(request,domain):
auth=request.values.get("server_auth")
if auth:
logging.info("get_server auth: %s"%auth)
id,auth=auth.split("-")
server=get_by_iid("server|%s"%id)
if server and gf(server,"auth")==auth: return server
def get_referrer(self,ip):
referrer=get_cookie(self,"referrer") or ip.referrer
def get_referrer(request,ip):
referrer=get_cookie(request,"referrer") or ip.referrer
if referrer and referrer.replace('"',"")[0] in ["0","1","2","3","4","5","6","7","8","9"]: referrer=get_by_iid("user|%s"%referrer.replace('"',""))
elif referrer: referrer=get_owner(referrer.replace('"',""))
return referrer
@@ -1362,7 +1367,7 @@ def increase_characterth():
try: main.put()
except: logTrace()
def get_new_auth(self,user,domain):
def get_new_auth(request,user,domain):
auth=randomStr(20)
user.info.auths=gf(user,"auths",[])
user.info.last_auth=datetime.now()
@@ -1372,7 +1377,7 @@ def get_new_auth(self,user,domain):
return auth
def download_by_iid(iid):
result=urlfetch.fetch(url="https://twodimensionalgame.appspot.com/tasks/download",payload=urllib.urlencode({"password":ELEMENT_PASSWORD,"iid":iid}),
result=urlfetch.fetch(url="https://twodimensionalgame.appspot.com/tasks/download",payload=urlencode({"password":ELEMENT_PASSWORD,"iid":iid}),
method=urlfetch.POST,headers={'Content-Type':'application/x-www-form-urlencoded'},validate_certificate=False)
return cPickle.loads(result.content)
@@ -1382,7 +1387,7 @@ def post_task(task,element=None,element2=None,element3=None,logging=None):
element=element and cPickle.dumps(element) or ""
element2=element2 and cPickle.dumps(element2) or ""
element3=element3 and cPickle.dumps(element3) or ""
result=urlfetch.fetch(url="https://twodimensionalgame.appspot.com/tasks/post",payload=urllib.urlencode({"p":SDK_UPLOAD_PASSWORD,"operation":"upload","obj1":element,"obj2":element2,"obj3":element3,"task_code":task}),
result=urlfetch.fetch(url="https://twodimensionalgame.appspot.com/tasks/post",payload=urlencode({"p":SDK_UPLOAD_PASSWORD,"operation":"upload","obj1":element,"obj2":element2,"obj3":element3,"task_code":task}),
method=urlfetch.POST,headers={'Content-Type': 'application/x-www-form-urlencoded'},validate_certificate=False)
result=cPickle.loads(str(result.content))
logging.info("post_task result: %s"%result)
@@ -1394,10 +1399,10 @@ def safe_commit(element):
def domain_routine(domain):
pass #GTODO: Implement
def delete_phrase_mark(type,phrase,async=False):
def delete_phrase_mark(type,phrase,_async=False):
ndb.Key(MarkedPhrase,dgt(type,phrase)).delete()
def mark_phrase(owner,phrase_type,phrase,async=False):
def mark_phrase(owner,phrase_type,phrase,_async=False):
MarkedPhrase(key=ndb.Key(MarkedPhrase,dgt(phrase_type,phrase)),type=phrase_type,phrase=str_or_unicode(phrase),owner=to_string_key(owner)).put()
def marker_check(id):
@@ -1407,7 +1412,7 @@ def marker_check(id):
return True
return ndb.transaction(marker_transaction,xg=True,retries=12)
def whtml(self,path,**dct):
def whtml(request,path,**dct):
#path = os.path.join(os.path.dirname(__file__), pth)
start_time=datetime.now()
template = j2.get_template(path)
@@ -1418,16 +1423,21 @@ def whtml(self,path,**dct):
response=response.replace('="/','="%s/'%dct["domain"].secure_base_url).replace("='/","='%s/"%dct["domain"].secure_base_url)
else:
response=response.replace('="/','="%s/'%dct["domain"].base_url).replace("='/","='%s/"%dct["domain"].base_url)
self.response.out.write(response)
request.response.set_data(response)
return request.response
def shtml(path,**dct):
#path = os.path.join(os.path.dirname(__file__), pth)
start_time=datetime.now()
template = j2.get_template(path)
if mssince(start_time)>250: logging.warning("JINJA2 WARNING %s took %s ms"%(path,mssince(start_time)))
#html=template.render(dct)
#if dct.pop("convert_onclicks",""): html=html.replace('onclick="','ontap="') #experimental [28/05/15]
return template.render(dct)
response=template.render(dct)
if dct.get("domain","") and dct["domain"].electron:
if dct["domain"].https:
response=response.replace('="/','="%s/'%dct["domain"].secure_base_url).replace("='/","='%s/"%dct["domain"].secure_base_url)
else:
response=response.replace('="/','="%s/'%dct["domain"].base_url).replace("='/","='%s/"%dct["domain"].base_url)
return response
def pre_put_hook(self):
try:
@@ -1449,10 +1459,10 @@ def arr_arr_same(ar1,ar2):
if not d.get(e): return False
return True
def delete_phrase_mark(type,phrase,async=False):
def delete_phrase_mark(type,phrase,_async=False):
ndb.Key(MarkedPhrase,dgt(type,phrase)).delete()
def mark_phrase(owner,phrase_type,phrase,async=False):
def mark_phrase(owner,phrase_type,phrase,_async=False):
MarkedPhrase(key=ndb.Key(MarkedPhrase,dgt(phrase_type,phrase)),type=phrase_type,phrase=str_or_unicode(phrase),owner=to_string_key(owner)).put()
def to_id(id):
@@ -1468,7 +1478,7 @@ def to_id(id):
def k_factory(self,t=""):
if t=="h":
the_key=self.key.id()
if type(the_key) in [types.IntType,types.LongType]: return the_key
if is_int(the_key): return the_key
return string_to_int(the_key)
if t=="e": return "%s"%has_key(self) or "-1" #as a placeholder mainly for the StuffHandler routines [28/08/14]
if t=="s": the_key=has_key(self)
@@ -1476,11 +1486,14 @@ def k_factory(self,t=""):
try: u"%s"%the_key
except: the_key=the_key.decode("utf-8")
if t in ["i","s",1]: return dmt(model_name(self),the_key)
return unicode(the_key)
try:
return unicode(the_key)
except:
return str(the_key) #python3 [27/01/24]
def to_model(string):
d=globals()
if d.has_key(string.title()): return d[string.title()]
if string.title() in d: return d[string.title()]
for e in d.keys():
try:
if len(e) and e[0].isupper() and e.lower()==string.lower(): return d[e]
@@ -1489,30 +1502,30 @@ def to_model(string):
def dgt(*list,**dct):
lst=[]
for i in range(0,len(list)): lst.append(to_str(list[i]))
if dct.has_key("sort"): lst.sort()
if "sort" in dct: lst.sort()
return "-".join(lst)
def dge(*list,**dct):
lst=[]
for i in range(0,len(list)): lst.append(to_str(list[i]))
if dct.has_key("sort"): lst.sort()
if "sort" in dct: lst.sort()
return "".join(lst)
def dse(*list,**dct):
lst=[]
for i in range(0,len(list)): lst.append(to_str(list[i]))
if dct.has_key("sort"): lst.sort()
if "sort" in dct: lst.sort()
return " ".join(lst)
def dmt(*list,**dct):
lst=[]
for i in range(0,len(list)): lst.append(to_str(list[i]))
if dct.has_key("sort"): lst.sort()
if "sort" in dct: lst.sort()
return "|".join(lst)
def to_string_key(element):
if type(element) in [types.IntType,types.LongType]: return str(element)
if type(element) in types.StringTypes: return element
if is_int(element): return str(element)
if is_string(element): return element
return element.k()
def str_or_unicode(s):
@@ -1527,6 +1540,18 @@ def model_name(item):
return item.get("base_type",item.get("type",""))
return item._class_name().lower()
def is_int(i):
if 1/2 == 0:
return type(i) in [types.IntType,types.LongType]
else:
return isinstance(i,int)
def is_string(s):
if 1/2 == 0:
return isinstance(s, basestring)
else:
return isinstance(s, str)
def is_array(a):
if hasattr(a, "__len__"): return True
@@ -1585,7 +1610,7 @@ def calculate_tutorial_step(user_data):
for i in xrange(len(docs["tutorial"])):
done=True
for key in docs["tutorial"][i]["tasks"]:
if not marked.has_key(key): done=False
if key not in marked: done=False
if not done and user_data.info.tutorial_step>i:
user_data.info.tutorial_step=i
break
@@ -1640,11 +1665,11 @@ def to_str(s):
def split_iid(s):
return s.split("|",1)
def get_by_iid(iid,async=False,no_cache=False):
def get_by_iid(iid,_async=False,no_cache=False):
mname,id=split_iid(iid)
try:
k=ndb.Key(to_model(mname),to_id(id))
if async: return k.get_async()
if _async: return k.get_async()
else:
if no_cache: return k.get(use_cache=False)
return k.get()
@@ -1653,7 +1678,7 @@ def get_by_iid(iid,async=False,no_cache=False):
return None
def get_by_iid_async(iid):
return get_by_iid(iid,async=True)
return get_by_iid(iid,_async=True)
def purify_email(email,check=True):
email=email.replace(" ","").replace("\t","").replace("\n","").replace("\r","")
@@ -1669,16 +1694,16 @@ def purify_email(email,check=True):
return email
def gf(element,name,default=None): # as in getattr info fast :)
if not hasattr(element,"info") or type(element.info)!=types.InstanceType: return getattr(element,name,default)
if not hasattr(element,"info") or type(element.info)!=type(GG()): return getattr(element,name,default)
return getattr(element.info,name,default)
def gt(element,name,default=None):
return getattr(element,name,default)
def gdmul(self,*lst):
def gdmul(request,*lst):
gg=[]
for l in lst:
gg.append(self.request.get(l))
gg.append(request.values.get(l))
if len(gg)==1: return gg[0]
return gg
@@ -1714,14 +1739,15 @@ def jsuccess(self,message,duration=0):
if not getattr(self,"_cjsons",0): self._cjsons=[]
self._cjsons.append({"type":"success","message":message,"duration":duration})
def jhtml(self,jsn=[]):
if not self: return
if getattr(self,"_cjsons",0):
self._cjsons.extend(jsn)
def jhtml(request,jsn=[]):
if not request: return
if getattr(request,"_cjsons",0):
request._cjsons.extend(jsn)
#logging.info("CJSONS %s"%self._cjsons)
self.response.out.write(json.dumps(self._cjsons))
request.response.set_data(json.dumps(self._cjsons))
else:
self.response.out.write(json.dumps(jsn))
request.response.set_data(json.dumps(jsn))
return request.response
def jhtmle(self,err):
if not self: return
@@ -1789,25 +1815,25 @@ def log_trace(place="",logger=None):
logger.error("\n\n<<<<<<<<<< log_trace %s %s>>>>>>>>>>"%(exc_type,place),exc_info=sys.exc_info())
logger.error("\n")
def get_domain(self=None):
def get_domain(request=None):
if not is_sdk:
return live_domain
else:
if self:
return (urlparse(self.request.url)).hostname
if request:
return (urlparse(request.url)).hostname
else:
return sdk_domain
def get_cookie(self,name):
return self.request.cookies.get(name)
def get_cookie(request,name):
return request.cookies.get(name)
def set_cookie(self,name,value):
hostname=get_domain(self)
self.response.set_cookie(name,to_str(value),max_age=86400*365*5, path='/',domain='.%s'%(hostname),secure=secure_cookies)
def set_cookie(request,name,value):
hostname=get_domain(request)
request.response.set_cookie(name,to_str(value),max_age=86400*365*5, path='/',domain='.%s'%(hostname),secure=secure_cookies)
def delete_cookie(self,name):
hostname=get_domain(self)
self.response.delete_cookie(name,path='/',domain='.%s'%(hostname))
def delete_cookie(request,name):
hostname=get_domain(request)
request.response.delete_cookie(name,path='/',domain='.%s'%(hostname))
class StrLogHandler(logging.Handler):
def __init__(self, output):
@@ -1872,9 +1898,9 @@ def randomStr(length,digits_only=False,lowercase_only=False):
for i in range(0,length+1): s+=chars[random.randrange(0,len(chars))]
return s
def add_event(element,type,tags,info=None,backup=False,async=False,self=None,rowner=None,selfic=None):
def add_event(element,type,tags,info=None,backup=False,_async=False,self=None,rowner=None,selfic=None):
logging.info("add_event %s %s %s"%(element and element.k('i'),type,tags))
if backup and element: backup=backup_item(element,async=True)
if backup and element: backup=backup_item(element,_async=True)
else: backup=None
if not info: info=GG()
if type not in tags: tags.append(type)
@@ -1888,13 +1914,13 @@ def add_event(element,type,tags,info=None,backup=False,async=False,self=None,row
event.item_id=element.k('i')
if hasattr(element,"region"): event.info.e_name="%s %s"%(element.region,element.name)
elif hasattr(element,"name"): event.info.e_name=element.name
if async: return event.put_async()
if _async: return event.put_async()
else:
if backup: backup.wait()
event.put()
return event
def backup_item(element,rkey=None,async=False):
def backup_item(element,rkey=None,_async=False):
logging.info("backup_item %s"%element.k('i'))
#if not has_key(element): logging.error("Backup: Element doesn't have a key"); return
if rkey: backup=Backup(key=ndb.Key(Backup,rkey))
@@ -1909,7 +1935,7 @@ def backup_item(element,rkey=None,async=False):
setattr(backup,k,i)
backup.backup_info=cGG(original_properties=element._properties)
backup.backup_created=datetime.now()
if async: return backup.put_async()
if _async: return backup.put_async()
else:
backup.put()
return backup
@@ -1948,7 +1974,10 @@ _js_escapes = (_base_js_escapes +
@jrf
def escapejs(value):
"""Hex encodes characters for use in JavaScript strings."""
if not isinstance(value, basestring):
base=str
if 1/2 == 0:
base=basestring
if not isinstance(value, base):
value = str(value)
for bad, good in _js_escapes:
@@ -2140,7 +2169,7 @@ def fetch_url(url_u,**dct):
else: url_u="https://adventure.land"+url_u
if dct or dct.pop("use_post",""):
encode_unicodes(dct)
data = urllib.urlencode(dct)
data = urlencode(dct)
return urlfetch.fetch(url=url_u,payload=data,method=urlfetch.POST,validate_certificate=is_production).content
else:
return urlfetch.fetch(url=url_u,method=urlfetch.GET,validate_certificate=is_production).content
@@ -2148,9 +2177,9 @@ def fetch_url(url_u,**dct):
def fetch_url_async(url_u,**dct):
rpc=urlfetch.create_rpc(deadline=40)
if dct.pop("use_get",""):
urlfetch.make_fetch_call(rpc,url_u+"?"+urllib.urlencode(dct),method=urlfetch.GET,validate_certificate=False)
urlfetch.make_fetch_call(rpc,url_u+"?"+urlencode(dct),method=urlfetch.GET,validate_certificate=False)
else:
data = urllib.urlencode(dct)
data = urlencode(dct)
urlfetch.make_fetch_call(rpc,url_u,payload=data,method=urlfetch.POST,headers={'Content-Type': 'application/x-www-form-urlencoded'},validate_certificate=False)
return rpc
@@ -2165,7 +2194,7 @@ def send_email(domain,email="kaansoral@gmail.com",title="Default Title",html="De
ses=amazon_ses.AmazonSES(secrets.amazon_ses_user,secrets.amazon_ses_key)
#logging.info(ses.listVerifiedEmailAddresses().members)
try: ses.sendEmail("pr@createandspread.com","kaansoral@gmail.com",message)
except amazon_ses.AmazonError,e:
except amazon_ses.AmazonError as e:
logging.info(e.errorType); logging.info(e.code); logging.info(e.message);
log_trace()
elif always_amazon_ses:
@@ -2176,7 +2205,7 @@ def send_email(domain,email="kaansoral@gmail.com",title="Default Title",html="De
ses=amazon_ses.AmazonSES(secrets.amazon_ses_user,secrets.amazon_ses_key)
#logging.info(ses.listVerifiedEmailAddresses().members)
try: ses.sendEmail("hello@adventure.land",email,message)
except amazon_ses.AmazonError,e:
except amazon_ses.AmazonError as e:
logging.info(e.errorType); logging.info(e.code); logging.info(e.message);
log_trace()
else:
@@ -2187,13 +2216,20 @@ def send_email(domain,email="kaansoral@gmail.com",title="Default Title",html="De
import hmac,hashlib
from struct import Struct
from operator import xor
from itertools import izip, starmap
from itertools import starmap
try:
from itertools import izip
except:
izip=zip
_pack_int = Struct('>I').pack
#https://code.google.com/p/googleappengine/issues/detail?id=5303 and https://github.com/mitsuhiko/python-pbkdf2/blob/master/pbkdf2.py
def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None):
"""Like :func:`pbkdf2_bin` but returns a hex encoded string."""
return pbkdf2_bin(data, salt, iterations, keylen, hashfunc).encode('hex')
if 1/2==0:
return pbkdf2_bin(data, salt, iterations, keylen, hashfunc).encode('hex')
else:
pbkdf2_bin(data, salt, iterations, keylen, hashfunc).encode("utf-8").hex()
def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None):
"""Returns a binary digest for the PBKDF2 hash algorithm of `data`
@@ -2206,13 +2242,22 @@ def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None):
def _pseudorandom(x, mac=mac):
h = mac.copy()
h.update(x)
return map(ord, h.digest())
if 1/2==0:
return map(ord, h.digest())
else:
return map(lambda x:x, h.digest())
buf = []
for block in xrange(1, -(-keylen // mac.digest_size) + 1):
rv = u = _pseudorandom(salt + _pack_int(block))
for i in xrange(iterations - 1):
u = _pseudorandom(''.join(map(chr, u)))
rv = starmap(xor, izip(rv, u))
if 1/2 == 0:
rv = u = _pseudorandom(salt + _pack_int(block))
for i in xrange(iterations - 1):
u = _pseudorandom(''.join(map(chr, u)))
rv = starmap(xor, izip(rv, u))
else:
rv = u = _pseudorandom(salt.encode(encoding = 'UTF-8', errors = 'strict') + _pack_int(block))
for i in xrange(iterations - 1):
u = _pseudorandom(''.join(map(chr, u)).encode(encoding = 'UTF-8', errors = 'strict'))
rv = starmap(xor, izip(rv, u))
buf.extend(rv)
return ''.join(map(chr, buf))[:keylen]
+6 -1
View File
@@ -1,4 +1,7 @@
import httplib
try:
import httplib
except:
import http.client as httplib #for python3
import urllib
import hashlib
import hmac
@@ -7,6 +10,8 @@ import base64
from datetime import datetime
from xml.etree.ElementTree import XML
if 1/2 != 0: basestring=str
def encodeUnicodez(args):
if args and type(args)==type({}):
for key,value in args.items():
+343
View File
@@ -0,0 +1,343 @@
try:
import http.client
except:
import http.client as httplib #for python3
import urllib.request, urllib.parse, urllib.error
import hashlib
import hmac
import logging
import base64
from datetime import datetime
from xml.etree.ElementTree import XML
if 1/2 != 0: str=str
def encodeUnicodez(args):
if args and type(args)==type({}):
for key,value in list(args.items()):
if type(value)==type(""):
args[key]=value.encode("utf-8")
log = logging.getLogger(__name__)
class AmazonSES:
def __init__(self, accessKeyID, secretAccessKey):
self._accessKeyID = accessKeyID
self._secretAccessKey = secretAccessKey
self._responseParser = AmazonResponseParser()
def _getSignature(self, dateValue):
h = hmac.new(key=self._secretAccessKey, msg=dateValue, digestmod=hashlib.sha256)
return base64.b64encode(h.digest()).decode()
def _getHeaders(self,oparams,params):
#headers = { 'Content-type': 'application/x-www-form-urlencoded' }
#d = datetime.utcnow()
#dateValue = d.strftime('%a, %d %b %Y %H:%M:%S GMT')
#headers['Date'] = dateValue
#signature = self._getSignature(dateValue)
#headers['X-Amzn-Authorization'] = 'AWS3-HTTPS AWSAccessKeyId=%s, Algorithm=HMACSHA256, Signature=%s' % (self._accessKeyID, signature)
def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning
# Read AWS access key from env. variables or configuration file. Best practice is NOT
# to embed credentials in code.
access_key = self._accessKeyID
secret_key = self._secretAccessKey
# Create a date for headers and the credential string
t = datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope
# ************* TASK 1: CREATE A CANONICAL REQUEST *************
# http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
# Step 1 is to define the verb (GET, POST, etc.)--already done.
# Step 2: Create canonical URI--the part of the URI from domain to query
# string (use '/' if no path)
content_type='application/x-www-form-urlencoded'
service = 'ec2'; service='email'
host = 'email.us-east-1.amazonaws.com'
region = 'us-east-1'
endpoint = 'https://email.us-east-1.amazonaws.com'
canonical_uri = '/'
method='POST'
# Step 3: Create the canonical query string. In this example (a GET request),
# request parameters are in the query string. Query string values must
# be URL-encoded (space=%20). The parameters must be sorted by name.
# For this example, the query string is pre-formatted in the request_parameters variable.
#canonical_querystring = request_parameters = 'Action=%s'%oparams['Action']
canonical_querystring=params
canonical_querystring=''
# Step 4: Create the canonical headers and signed headers. Header names
# must be trimmed and lowercase, and sorted in code point order from
# low to high. Note that there is a trailing \n.
canonical_headers = 'host:' + host + '\n' + 'x-amz-date:' + amzdate + '\n'
# Step 5: Create the list of signed headers. This lists the headers
# in the canonical_headers list, delimited with ";" and in alpha order.
# Note: The request can include any headers; canonical_headers and
# signed_headers lists those that you want to be included in the
# hash of the request. "Host" and "x-amz-date" are always required.
signed_headers = 'host;x-amz-date'
# Step 6: Create payload hash (hash of the request body content). For GET
# requests, the payload is an empty string ("").
payload_hash = hashlib.sha256((params).encode('utf-8')).hexdigest()
# Step 7: Combine elements to create canonical request
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
# ************* TASK 2: CREATE THE STRING TO SIGN*************
# Match the algorithm to the hashing algorithm you use, either SHA-1 or
# SHA-256 (recommended)
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
logging.info(string_to_sign)
# ************* TASK 3: CALCULATE THE SIGNATURE *************
# Create the signing key using the function defined above.
signing_key = getSignatureKey(secret_key, datestamp, region, service)
# Sign the string_to_sign using the signing_key
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
headers = {'Content-Type':content_type,
#'X-Amz-Algorithm':'AWS4-HMAC-SHA256',
'X-Amz-Date':amzdate,
'Authorization':authorization_header}
return headers
def _performAction(self, actionName, params=None):
if not params:
params = {}
params['Action'] = actionName
oparams=params
#https://email.us-east-1.amazonaws.com/
conn = http.client.HTTPSConnection('email.us-east-1.amazonaws.com')
encodeUnicodez(params)
params = urllib.parse.urlencode(params)
conn.request('POST', '/', params, self._getHeaders(oparams,params))
response = conn.getresponse()
responseResult = response.read()
conn.close()
return self._responseParser.parse(actionName, response.status, response.reason, responseResult)
def verifyEmailAddress(self, emailAddress):
params = { 'EmailAddress': emailAddress }
return self._performAction('VerifyEmailAddress', params)
def deleteVerifiedEmailAddress(self, emailAddress):
params = { 'EmailAddress': emailAddress }
return self._performAction('DeleteVerifiedEmailAddress', params)
def getSendQuota(self):
return self._performAction('GetSendQuota')
def getSendStatistics(self):
return self._performAction('GetSendStatistics')
def listVerifiedEmailAddresses(self):
return self._performAction('ListVerifiedEmailAddresses')
def sendEmail(self, source, toAddresses, message, replyToAddresses=None, returnPath=None, ccAddresses=None, bccAddresses=None):
params = { 'Source': source }
for objName, addresses in zip(["ToAddresses", "CcAddresses", "BccAddresses"], [toAddresses, ccAddresses, bccAddresses]):
if addresses:
if not isinstance(addresses, str) and getattr(addresses, '__iter__', False):
for i, address in enumerate(addresses, 1):
params['Destination.%s.member.%d' % (objName, i)] = address
else:
params['Destination.%s.member.1' % objName] = addresses
if not returnPath:
returnPath = source
params['ReturnPath'] = returnPath
params['Message.Subject.Charset'] = message.charset
params['Message.Subject.Data'] = message.subject
if message.bodyText:
params['Message.Body.Text.Charset'] = message.charset
params['Message.Body.Text.Data'] = message.bodyText
if message.bodyHtml:
params['Message.Body.Html.Charset'] = message.charset
params['Message.Body.Html.Data'] = message.bodyHtml
return self._performAction('SendEmail', params)
class EmailMessage:
def __init__(self):
self.charset = 'UTF-8'
self.subject = None
self.bodyHtml = None
self.bodyText = None
class AmazonError(Exception):
def __init__(self, errorType, code, message):
self.errorType = errorType
self.code = code
self.message = message
logging.info(message)
class AmazonAPIError(Exception):
def __init__(self, message):
self.message = message
class AmazonResult:
def __init__(self, requestId):
self.requestId = requestId
class AmazonSendEmailResult(AmazonResult):
def __init__(self, requestId, messageId):
self.requestId = requestId
self.messageId = messageId
class AmazonSendQuota(AmazonResult):
def __init__(self, requestId, max24HourSend, maxSendRate, sentLast24Hours):
self.requestId = requestId
self.max24HourSend = max24HourSend
self.maxSendRate = maxSendRate
self.sentLast24Hours = sentLast24Hours
class AmazonSendDataPoint:
def __init__(self, bounces, complaints, deliveryAttempts, rejects, timestamp):
self.bounces = bounces
self.complaints = complaints
self.deliveryAttempts = deliveryAttempts
self.rejects = rejects
self.timestamp = timestamp
class AmazonSendStatistics(AmazonResult):
def __init__(self, requestId):
self.requestId = requestId
self.members = []
class AmazonVerifiedEmails(AmazonSendStatistics):
pass
class AmazonResponseParser:
class XmlResponse:
def __init__(self, str):
self._rootElement = XML(str)
self._namespace = self._rootElement.tag[1:].split("}")[0]
def checkResponseName(self, name):
if self._rootElement.tag == self._fixTag(self._namespace, name):
return True
else:
raise AmazonAPIError('ErrorResponse is invalid.')
def checkActionName(self, actionName):
if self._rootElement.tag == self._fixTag(self._namespace, ('%sResponse' % actionName)):
return True
else:
raise AmazonAPIError('Response of action "%s" is invalid.' % actionName)
def getChild(self, *itemPath):
node = self._findNode(self._rootElement, self._namespace, *itemPath)
if node != None:
return node
else:
raise AmazonAPIError('Node with the specified path was not found.')
def getChildText(self, *itemPath):
node = self.getChild(*itemPath)
return node.text
def _fixTag(self, namespace, tag):
return '{%s}%s' % (namespace, tag)
def _findNode(self, rootElement, namespace, *args):
match = '.'
for s in args:
match += '/{%s}%s' % (namespace, s)
return rootElement.find(match)
def __init__(self):
self._simpleResultActions = ['DeleteVerifiedEmailAddress', 'VerifyEmailAddress']
def _parseSimpleResult(self, actionName, xmlResponse):
if xmlResponse.checkActionName(actionName):
requestId = xmlResponse.getChildText('ResponseMetadata', 'RequestId')
return AmazonResult(requestId)
def _parseSendQuota(self, actionName, xmlResponse):
if xmlResponse.checkActionName(actionName):
requestId = xmlResponse.getChildText('ResponseMetadata', 'RequestId')
value = xmlResponse.getChildText('GetSendQuotaResult', 'Max24HourSend')
max24HourSend = float(value)
value = xmlResponse.getChildText('GetSendQuotaResult', 'MaxSendRate')
maxSendRate = float(value)
value = xmlResponse.getChildText('GetSendQuotaResult', 'SentLast24Hours')
sentLast24Hours = float(value)
return AmazonSendQuota(requestId, max24HourSend, maxSendRate, sentLast24Hours)
#def _parseSendStatistics(self, actionName, xmlResponse):
# if xmlResponse.checkActionName(actionName):
# requestId = xmlResponse.getChildText('ResponseMetadata', 'RequestId')
def _parseListVerifiedEmails(self, actionName, xmlResponse):
if xmlResponse.checkActionName(actionName):
requestId = xmlResponse.getChildText('ResponseMetadata', 'RequestId')
node = xmlResponse.getChild('ListVerifiedEmailAddressesResult', 'VerifiedEmailAddresses')
result = AmazonVerifiedEmails(requestId)
for addr in node:
result.members.append(addr.text)
return result
def _parseSendEmail(self, actionName, xmlResponse):
if xmlResponse.checkActionName(actionName):
requestId = xmlResponse.getChildText('ResponseMetadata', 'RequestId')
messageId = xmlResponse.getChildText('SendEmailResult', 'MessageId')
return AmazonSendEmailResult(requestId, messageId)
def _raiseError(self, xmlResponse):
if xmlResponse.checkResponseName('ErrorResponse'):
errorType = xmlResponse.getChildText('Error', 'Type')
code = xmlResponse.getChildText('Error', 'Code')
message = xmlResponse.getChildText('Error', 'Message')
raise AmazonError(errorType, code, message)
def parse(self, actionName, statusCode, reason, responseResult):
xmlResponse = self.XmlResponse(responseResult)
log.info('Response status code: %s, reason: %s', statusCode, reason)
log.debug(responseResult)
result = None
if statusCode != 200:
self._raiseError(xmlResponse)
else:
if actionName in self._simpleResultActions:
result = self._parseSimpleResult(actionName, xmlResponse)
elif actionName in ['SendEmail']:
result = self._parseSendEmail(actionName, xmlResponse)
elif actionName == 'GetSendQuota':
result = self._parseSendQuota(actionName, xmlResponse)
#elif actionName == 'GetSendStatistics':
# result = self._parseSendStatistics(actionName, xmlResponse)
elif actionName == 'ListVerifiedEmailAddresses':
result = self._parseListVerifiedEmails(actionName, xmlResponse)
else:
raise AmazonAPIError('Action %s is not supported. Please contact: vladimir@tagmask.com' % (actionName,))
return result
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
"""
get_image_size.py
+2 -2
View File
@@ -2,8 +2,8 @@ import calendar
import datetime
import platform
import time
import urllib
import urlparse
#import urllib
#import urlparse
import warnings
import libraries.stripe
+2
View File
@@ -4,6 +4,8 @@ import textwrap
import warnings
import email
if 1/2 != 0: basestring=str
from libraries.stripe import error, util
+2
View File
@@ -2,6 +2,8 @@ import urllib
import warnings
import sys
if 1/2 != 0: basestring=str
from libraries.stripe import api_requestor, error, util, upload_api_base
+2
View File
@@ -6,6 +6,8 @@ import string
import sys
import unittest2
if 1/2 != 0: basestring=str
from mock import patch, Mock
import libraries.stripe
@@ -1,6 +1,8 @@
import sys
import unittest2
if 1/2 != 0: basestring=str
from mock import Mock, patch
import libraries.stripe
@@ -4,6 +4,8 @@ import sys
import unittest2
import libraries.stripe
if 1/2 != 0: basestring=str
from mock import patch
from libraries.stripe.test.helper import (StripeTestCase, NOW, DUMMY_CHARGE, DUMMY_CARD)
+8 -2
View File
@@ -10,13 +10,19 @@ try:
# When cStringIO is available
import cStringIO as StringIO
except ImportError:
import StringIO
try:
import StringIO
except:
from io import StringIO #for Python 3
try:
from urlparse import parse_qsl
except ImportError:
# Python < 2.6
from cgi import parse_qsl
try:
from cgi import parse_qsl
except:
from urllib.parse import parse_qsl #for Python 3
try:
import json
+78
View File
@@ -0,0 +1,78 @@
# Stripe Python bindings
# API docs at http://stripe.com/docs/api
# Authors:
# Patrick Collison <patrick@stripe.com>
# Greg Brockman <gdb@stripe.com>
# Andrew Metcalf <andrew@stripe.com>
# Configuration variables
api_key = None
api_base = 'https://api.stripe.com'
upload_api_base = 'https://uploads.stripe.com'
api_version = None
verify_ssl_certs = True
default_http_client = None
# Resource
from libraries.stripe.resource import ( # noqa
Account,
AlipayAccount,
ApplicationFee,
Balance,
BalanceTransaction,
BankAccount,
BitcoinReceiver,
BitcoinTransaction,
Card,
Charge,
CountrySpec,
Coupon,
Customer,
Dispute,
Event,
FileUpload,
Invoice,
InvoiceItem,
Order,
OrderReturn,
Plan,
Product,
Recipient,
Refund,
SKU,
Subscription,
ThreeDSecure,
Token,
Transfer)
# Error imports. Note that we may want to move these out of the root
# namespace in the future and you should prefer to access them via
# the fully qualified `stripe.error` module.
from libraries.stripe.error import ( # noqa
APIConnectionError,
APIError,
AuthenticationError,
RateLimitError,
CardError,
InvalidRequestError,
StripeError)
# DEPRECATED: These imports will be moved out of the root stripe namespace
# in version 2.0
from libraries.stripe.version import VERSION # noqa
from libraries.stripe.api_requestor import APIRequestor # noqa
from libraries.stripe.resource import ( # noqa
APIResource,
CreateableAPIResource,
DeletableAPIResource,
ListObject,
ListableAPIResource,
SingletonAPIResource,
StripeObject,
StripeObjectEncoder,
UpdateableAPIResource,
convert_to_stripe_object)
from libraries.stripe.util import json, logger # noqa
+342
View File
@@ -0,0 +1,342 @@
import calendar
import datetime
import platform
import time
import urllib.request, urllib.parse, urllib.error
import urllib.parse
import warnings
import libraries.stripe
from libraries.stripe import error, http_client, version, util
from libraries.stripe.multipart_data_generator import MultipartDataGenerator
def _encode_datetime(dttime):
if dttime.tzinfo and dttime.tzinfo.utcoffset(dttime) is not None:
utc_timestamp = calendar.timegm(dttime.utctimetuple())
else:
utc_timestamp = time.mktime(dttime.timetuple())
return int(utc_timestamp)
def _encode_nested_dict(key, data, fmt='%s[%s]'):
d = {}
for subkey, subvalue in data.items():
d[fmt % (key, subkey)] = subvalue
return d
def _api_encode(data):
for key, value in data.items():
key = util.utf8(key)
if value is None:
continue
elif hasattr(value, 'stripe_id'):
yield (key, value.stripe_id)
elif isinstance(value, list) or isinstance(value, tuple):
for sv in value:
if isinstance(sv, dict):
subdict = _encode_nested_dict(key, sv, fmt='%s[][%s]')
for k, v in _api_encode(subdict):
yield (k, v)
else:
yield ("%s[]" % (key,), util.utf8(sv))
elif isinstance(value, dict):
subdict = _encode_nested_dict(key, value)
for subkey, subvalue in _api_encode(subdict):
yield (subkey, subvalue)
elif isinstance(value, datetime.datetime):
yield (key, _encode_datetime(value))
else:
yield (key, util.utf8(value))
def _build_api_url(url, query):
scheme, netloc, path, base_query, fragment = urllib.parse.urlsplit(url)
if base_query:
query = '%s&%s' % (base_query, query)
return urllib.parse.urlunsplit((scheme, netloc, path, query, fragment))
class APIRequestor(object):
def __init__(self, key=None, client=None, api_base=None, account=None):
self.api_base = api_base or libraries.stripe.api_base
self.api_key = key
self.stripe_account = account
from libraries.stripe import verify_ssl_certs as verify
self._client = client or libraries.stripe.default_http_client or \
http_client.new_default_http_client(verify_ssl_certs=verify)
@classmethod
def api_url(cls, url=''):
warnings.warn(
'The `api_url` class method of APIRequestor is '
'deprecated and will be removed in version 2.0.'
'If you need public access to this function, please email us '
'at support@stripe.com.',
DeprecationWarning)
return '%s%s' % (stripe.api_base, url)
@classmethod
def _deprecated_encode(cls, stk, key, value):
warnings.warn(
'The encode_* class methods of APIRequestor are deprecated and '
'will be removed in version 2.0. '
'If you need public access to this function, please email us '
'at support@stripe.com.',
DeprecationWarning, stacklevel=2)
stk.extend(_api_encode({key: value}))
@classmethod
def encode_dict(cls, stk, key, value):
cls._deprecated_encode(stk, key, value)
@classmethod
def encode_list(cls, stk, key, value):
cls._deprecated_encode(stk, key, value)
@classmethod
def encode_datetime(cls, stk, key, value):
cls._deprecated_encode(stk, key, value)
@classmethod
def encode_none(cls, stk, key, value):
cls._deprecated_encode(stk, key, value)
@classmethod
def encode(cls, d):
"""
Internal: encode a string for url representation
"""
warnings.warn(
'The `encode` class method of APIRequestor is deprecated and '
'will be removed in version 2.0.'
'If you need public access to this function, please email us '
'at support@stripe.com.',
DeprecationWarning)
return urllib.parse.urlencode(list(_api_encode(d)))
@classmethod
def build_url(cls, url, params):
warnings.warn(
'The `build_url` class method of APIRequestor is deprecated and '
'will be removed in version 2.0.'
'If you need public access to this function, please email us '
'at support@stripe.com.',
DeprecationWarning)
return _build_api_url(url, cls.encode(params))
def request(self, method, url, params=None, headers=None):
rbody, rcode, rheaders, my_api_key = self.request_raw(
method.lower(), url, params, headers)
resp = self.interpret_response(rbody, rcode, rheaders)
return resp, my_api_key
def handle_api_error(self, rbody, rcode, resp, rheaders):
try:
err = resp['error']
except (KeyError, TypeError):
raise error.APIError(
"Invalid response object from API: %r (HTTP response code "
"was %d)" % (rbody, rcode),
rbody, rcode, resp)
# Rate limits were previously coded as 400's with code 'rate_limit'
if rcode == 429 or (rcode == 400 and err.get('code') == 'rate_limit'):
raise error.RateLimitError(
err.get('message'), rbody, rcode, resp, rheaders)
elif rcode in [400, 404]:
raise error.InvalidRequestError(
err.get('message'), err.get('param'),
rbody, rcode, resp, rheaders)
elif rcode == 401:
raise error.AuthenticationError(
err.get('message'), rbody, rcode, resp,
rheaders)
elif rcode == 402:
raise error.CardError(err.get('message'), err.get('param'),
err.get('code'), rbody, rcode, resp,
rheaders)
else:
raise error.APIError(err.get('message'), rbody, rcode, resp,
rheaders)
def request_raw(self, method, url, params=None, supplied_headers=None):
"""
Mechanism for issuing an API call
"""
from libraries.stripe import api_version
if self.api_key:
my_api_key = self.api_key
else:
from libraries.stripe import api_key
my_api_key = api_key
if my_api_key is None:
raise error.AuthenticationError(
'No API key provided. (HINT: set your API key using '
'"stripe.api_key = <API-KEY>"). You can generate API keys '
'from the Stripe web interface. See https://stripe.com/api '
'for details, or email support@stripe.com if you have any '
'questions.')
abs_url = '%s%s' % (self.api_base, url)
encoded_params = urllib.parse.urlencode(list(_api_encode(params or {})))
if method == 'get' or method == 'delete':
if params:
abs_url = _build_api_url(abs_url, encoded_params)
post_data = None
elif method == 'post':
if supplied_headers is not None and \
supplied_headers.get("Content-Type") == \
"multipart/form-data":
generator = MultipartDataGenerator()
generator.add_params(params or {})
post_data = generator.get_post_data()
supplied_headers["Content-Type"] = \
"multipart/form-data; boundary=%s" % (generator.boundary,)
else:
post_data = encoded_params
else:
raise error.APIConnectionError(
'Unrecognized HTTP method %r. This may indicate a bug in the '
'Stripe bindings. Please contact support@stripe.com for '
'assistance.' % (method,))
ua = {
'bindings_version': version.VERSION,
'lang': 'python',
'publisher': 'stripe',
'httplib': self._client.name,
}
for attr, func in [['lang_version', platform.python_version],
['platform', platform.platform],
['uname', lambda: ' '.join(platform.uname())]]:
try:
val = func()
except Exception as e:
val = "!! %s" % (e,)
ua[attr] = val
headers = {
'X-Stripe-Client-User-Agent': util.json.dumps(ua),
'User-Agent': 'Stripe/v1 PythonBindings/%s' % (version.VERSION,),
'Authorization': 'Bearer %s' % (my_api_key,)
}
if self.stripe_account:
headers['Stripe-Account'] = self.stripe_account
if method == 'post':
headers['Content-Type'] = 'application/x-www-form-urlencoded'
if api_version is not None:
headers['Stripe-Version'] = api_version
if supplied_headers is not None:
for key, value in list(supplied_headers.items()):
headers[key] = value
rbody, rcode, rheaders = self._client.request(
method, abs_url, headers, post_data)
util.logger.info('%s %s %d', method.upper(), abs_url, rcode)
util.logger.debug(
'API request to %s returned (response code, response body) of '
'(%d, %r)',
abs_url, rcode, rbody)
return rbody, rcode, rheaders, my_api_key
def interpret_response(self, rbody, rcode, rheaders):
try:
if hasattr(rbody, 'decode'):
rbody = rbody.decode('utf-8')
resp = util.json.loads(rbody)
except Exception:
raise error.APIError(
"Invalid response body from API: %s "
"(HTTP response code was %d)" % (rbody, rcode),
rbody, rcode, rheaders)
if not (200 <= rcode < 300):
self.handle_api_error(rbody, rcode, resp, rheaders)
return resp
# Deprecated request handling. Will all be removed in 2.0
def _deprecated_request(self, impl, method, url, headers, params):
warnings.warn(
'The *_request functions of APIRequestor are deprecated and '
'will be removed in version 2.0. Please use the client classes '
' in `stripe.http_client` instead',
DeprecationWarning, stacklevel=2)
method = method.lower()
if method == 'get' or method == 'delete':
if params:
url = self.build_url(url, params)
post_data = None
elif method == 'post':
post_data = self.encode(params)
else:
raise error.APIConnectionError(
'Unrecognized HTTP method %r. This may indicate a bug in the '
'Stripe bindings. Please contact support@stripe.com for '
'assistance.' % (method,))
client = impl(verify_ssl_certs=self._client._verify_ssl_certs)
return client.request(method, url, headers, post_data)
def _deprecated_handle_error(self, impl, *args):
warnings.warn(
'The handle_*_error functions of APIRequestor are deprecated and '
'will be removed in version 2.0. Please use the client classes '
' in `stripe.http_client` instead',
DeprecationWarning, stacklevel=2)
client = impl(verify_ssl_certs=self._client._verify_ssl_certs)
return client._handle_request_error(*args)
def requests_request(self, meth, abs_url, headers, params):
from libraries.stripe.http_client import RequestsClient
return self._deprecated_request(RequestsClient, meth, abs_url,
headers, params)
def handle_requests_error(self, err):
from libraries.stripe.http_client import RequestsClient
return self._deprecated_handle_error(RequestsClient, err)
def pycurl_request(self, meth, abs_url, headers, params):
from libraries.stripe.http_client import PycurlClient
return self._deprecated_request(PycurlClient, meth, abs_url,
headers, params)
def handle_pycurl_error(self, err):
from libraries.stripe.http_client import PycurlClient
return self._deprecated_handle_error(PycurlClient, err)
def urlfetch_request(self, meth, abs_url, headers, params):
from libraries.stripe.http_client import UrlFetchClient
return self._deprecated_request(UrlFetchClient, meth, abs_url,
headers, params)
def handle_urlfetch_error(self, err, abs_url):
from libraries.stripe.http_client import UrlFetchClient
return self._deprecated_handle_error(UrlFetchClient, err, abs_url)
def urllib2_request(self, meth, abs_url, headers, params):
from libraries.stripe.http_client import Urllib2Client
return self._deprecated_request(Urllib2Client, meth, abs_url,
headers, params)
def handle_urllib2_error(self, err, abs_url):
from libraries.stripe.http_client import Urllib2Client
return self._deprecated_handle_error(Urllib2Client, err)
File diff suppressed because it is too large Load Diff
+74
View File
@@ -0,0 +1,74 @@
# Exceptions
import sys
class StripeError(Exception):
def __init__(self, message=None, http_body=None, http_status=None,
json_body=None, headers=None):
super(StripeError, self).__init__(message)
if http_body and hasattr(http_body, 'decode'):
try:
http_body = http_body.decode('utf-8')
except:
http_body = ('<Could not decode body as utf-8. '
'Please report to support@stripe.com>')
self._message = message
self.http_body = http_body
self.http_status = http_status
self.json_body = json_body
self.headers = headers or {}
self.request_id = self.headers.get('request-id', None)
def __unicode__(self):
if self.request_id is not None:
msg = self._message or "<empty message>"
return "Request {0}: {1}".format(self.request_id, msg)
else:
return self._message
if sys.version_info > (3, 0):
def __str__(self):
return self.__unicode__()
else:
def __str__(self):
return str(self).encode('utf-8')
class APIError(StripeError):
pass
class APIConnectionError(StripeError):
pass
class CardError(StripeError):
def __init__(self, message, param, code, http_body=None,
http_status=None, json_body=None, headers=None):
super(CardError, self).__init__(
message, http_body, http_status, json_body,
headers)
self.param = param
self.code = code
class InvalidRequestError(StripeError):
def __init__(self, message, param, http_body=None,
http_status=None, json_body=None, headers=None):
super(InvalidRequestError, self).__init__(
message, http_body, http_status, json_body,
headers)
self.param = param
class AuthenticationError(StripeError):
pass
class RateLimitError(StripeError):
pass
+297
View File
@@ -0,0 +1,297 @@
import os
import sys
import textwrap
import warnings
import email
if 1/2 != 0: str=str
from libraries.stripe import error, util
# - Requests is the preferred HTTP library
# - Google App Engine has urlfetch
# - Use Pycurl if it's there (at least it verifies SSL certs)
# - Fall back to urllib2 with a warning if needed
try:
import urllib.request, urllib.error, urllib.parse
except ImportError:
pass
try:
import pycurl
except ImportError:
pycurl = None
try:
import requests
except ImportError:
requests = None
else:
try:
# Require version 0.8.8, but don't want to depend on distutils
version = requests.__version__
major, minor, patch = [int(i) for i in version.split('.')]
except Exception:
# Probably some new-fangled version, so it should support verify
pass
else:
if (major, minor, patch) < (0, 8, 8):
sys.stderr.write(
'Warning: the Stripe library requires that your Python '
'"requests" library be newer than version 0.8.8, but your '
'"requests" library is version %s. Stripe will fall back to '
'an alternate HTTP library so everything should work. We '
'recommend upgrading your "requests" library. If you have any '
'questions, please contact support@stripe.com. (HINT: running '
'"pip install -U requests" should upgrade your requests '
'library to the latest version.)' % (version,))
requests = None
try:
from google.appengine.api import urlfetch
except ImportError:
urlfetch = None
def new_default_http_client(*args, **kwargs):
if urlfetch:
impl = UrlFetchClient
elif requests:
impl = RequestsClient
elif pycurl:
impl = PycurlClient
else:
impl = Urllib2Client
warnings.warn(
"Warning: the Stripe library is falling back to urllib2/urllib "
"because neither requests nor pycurl are installed. "
"urllib2's SSL implementation doesn't verify server "
"certificates. For improved security, we suggest installing "
"requests.")
return impl(*args, **kwargs)
class HTTPClient(object):
def __init__(self, verify_ssl_certs=True):
self._verify_ssl_certs = verify_ssl_certs
def request(self, method, url, headers, post_data=None):
raise NotImplementedError(
'HTTPClient subclasses must implement `request`')
class RequestsClient(HTTPClient):
name = 'requests'
def request(self, method, url, headers, post_data=None):
kwargs = {}
if self._verify_ssl_certs:
kwargs['verify'] = os.path.join(
os.path.dirname(__file__), 'data/ca-certificates.crt')
else:
kwargs['verify'] = False
try:
try:
result = requests.request(method,
url,
headers=headers,
data=post_data,
timeout=80,
**kwargs)
except TypeError as e:
raise TypeError(
'Warning: It looks like your installed version of the '
'"requests" library is not compatible with Stripe\'s '
'usage thereof. (HINT: The most likely cause is that '
'your "requests" library is out of date. You can fix '
'that by running "pip install -U requests".) The '
'underlying error was: %s' % (e,))
# This causes the content to actually be read, which could cause
# e.g. a socket timeout. TODO: The other fetch methods probably
# are susceptible to the same and should be updated.
content = result.content
status_code = result.status_code
except Exception as e:
# Would catch just requests.exceptions.RequestException, but can
# also raise ValueError, RuntimeError, etc.
self._handle_request_error(e)
return content, status_code, result.headers
def _handle_request_error(self, e):
if isinstance(e, requests.exceptions.RequestException):
msg = ("Unexpected error communicating with Stripe. "
"If this problem persists, let us know at "
"support@stripe.com.")
err = "%s: %s" % (type(e).__name__, str(e))
else:
msg = ("Unexpected error communicating with Stripe. "
"It looks like there's probably a configuration "
"issue locally. If this problem persists, let us "
"know at support@stripe.com.")
err = "A %s was raised" % (type(e).__name__,)
if str(e):
err += " with error message %s" % (str(e),)
else:
err += " with no error message"
msg = textwrap.fill(msg) + "\n\n(Network error: %s)" % (err,)
raise error.APIConnectionError(msg)
class UrlFetchClient(HTTPClient):
name = 'urlfetch'
def __init__(self, verify_ssl_certs=True, deadline=55):
self._verify_ssl_certs = verify_ssl_certs
# GAE requests time out after 60 seconds, so make sure to default
# to 55 seconds to allow for a slow Stripe
self._deadline = deadline
def request(self, method, url, headers, post_data=None):
try:
result = urlfetch.fetch(
url=url,
method=method,
headers=headers,
# Google App Engine doesn't let us specify our own cert bundle.
# However, that's ok because the CA bundle they use recognizes
# api.stripe.com.
validate_certificate=self._verify_ssl_certs,
deadline=self._deadline,
payload=post_data
)
except urlfetch.Error as e:
self._handle_request_error(e, url)
return result.content, result.status_code, result.headers
def _handle_request_error(self, e, url):
if isinstance(e, urlfetch.InvalidURLError):
msg = ("The Stripe library attempted to fetch an "
"invalid URL (%r). This is likely due to a bug "
"in the Stripe Python bindings. Please let us know "
"at support@stripe.com." % (url,))
elif isinstance(e, urlfetch.DownloadError):
msg = "There was a problem retrieving data from Stripe."
elif isinstance(e, urlfetch.ResponseTooLargeError):
msg = ("There was a problem receiving all of your data from "
"Stripe. This is likely due to a bug in Stripe. "
"Please let us know at support@stripe.com.")
else:
msg = ("Unexpected error communicating with Stripe. If this "
"problem persists, let us know at support@stripe.com.")
msg = textwrap.fill(msg) + "\n\n(Network error: " + str(e) + ")"
raise error.APIConnectionError(msg)
class PycurlClient(HTTPClient):
name = 'pycurl'
def parse_headers(self, data):
if '\r\n' not in data:
return {}
raw_headers = data.split('\r\n', 1)[1]
headers = email.message_from_string(raw_headers)
return dict((k.lower(), v) for k, v in dict(headers).items())
def request(self, method, url, headers, post_data=None):
s = util.StringIO.StringIO()
rheaders = util.StringIO.StringIO()
curl = pycurl.Curl()
if method == 'get':
curl.setopt(pycurl.HTTPGET, 1)
elif method == 'post':
curl.setopt(pycurl.POST, 1)
curl.setopt(pycurl.POSTFIELDS, post_data)
else:
curl.setopt(pycurl.CUSTOMREQUEST, method.upper())
# pycurl doesn't like unicode URLs
curl.setopt(pycurl.URL, util.utf8(url))
curl.setopt(pycurl.WRITEFUNCTION, s.write)
curl.setopt(pycurl.HEADERFUNCTION, rheaders.write)
curl.setopt(pycurl.NOSIGNAL, 1)
curl.setopt(pycurl.CONNECTTIMEOUT, 30)
curl.setopt(pycurl.TIMEOUT, 80)
curl.setopt(pycurl.HTTPHEADER, ['%s: %s' % (k, v)
for k, v in headers.items()])
if self._verify_ssl_certs:
curl.setopt(pycurl.CAINFO, os.path.join(
os.path.dirname(__file__), 'data/ca-certificates.crt'))
else:
curl.setopt(pycurl.SSL_VERIFYHOST, False)
try:
curl.perform()
except pycurl.error as e:
self._handle_request_error(e)
rbody = s.getvalue()
rcode = curl.getinfo(pycurl.RESPONSE_CODE)
return rbody, rcode, self.parse_headers(rheaders.getvalue())
def _handle_request_error(self, e):
if e[0] in [pycurl.E_COULDNT_CONNECT,
pycurl.E_COULDNT_RESOLVE_HOST,
pycurl.E_OPERATION_TIMEOUTED]:
msg = ("Could not connect to Stripe. Please check your "
"internet connection and try again. If this problem "
"persists, you should check Stripe's service status at "
"https://twitter.com/stripestatus, or let us know at "
"support@stripe.com.")
elif (e[0] in [pycurl.E_SSL_CACERT,
pycurl.E_SSL_PEER_CERTIFICATE]):
msg = ("Could not verify Stripe's SSL certificate. Please make "
"sure that your network is not intercepting certificates. "
"If this problem persists, let us know at "
"support@stripe.com.")
else:
msg = ("Unexpected error communicating with Stripe. If this "
"problem persists, let us know at support@stripe.com.")
msg = textwrap.fill(msg) + "\n\n(Network error: " + e[1] + ")"
raise error.APIConnectionError(msg)
class Urllib2Client(HTTPClient):
if sys.version_info >= (3, 0):
name = 'urllib.request'
else:
name = 'urllib2'
def request(self, method, url, headers, post_data=None):
if sys.version_info >= (3, 0) and isinstance(post_data, str):
post_data = post_data.encode('utf-8')
req = urllib.request.Request(url, post_data, headers)
if method not in ('get', 'post'):
req.get_method = lambda: method.upper()
try:
response = urllib.request.urlopen(req)
rbody = response.read()
rcode = response.code
headers = dict(response.info())
except urllib.error.HTTPError as e:
rcode = e.code
rbody = e.read()
headers = dict(e.info())
except (urllib.error.URLError, ValueError) as e:
self._handle_request_error(e)
lh = dict((k.lower(), v) for k, v in dict(headers).items())
return rbody, rcode, lh
def _handle_request_error(self, e):
msg = ("Unexpected error communicating with Stripe. "
"If this problem persists, let us know at support@stripe.com.")
msg = textwrap.fill(msg) + "\n\n(Network error: " + str(e) + ")"
raise error.APIConnectionError(msg)
+16
View File
@@ -0,0 +1,16 @@
import warnings
warnings.warn("The importers module is deprecated and will be removed in "
"version 2.0. Check the `util` module for json imports",
DeprecationWarning)
def import_json():
warnings.warn(
"'import_json function is deprecated and will be removed in version "
"2.0 of the Stripe python bindings. Please use"
"`from importer import json` instead'",
DeprecationWarning)
from libraries.stripe.util import json
return json
+76
View File
@@ -0,0 +1,76 @@
import random
import sys
import io
class MultipartDataGenerator(object):
def __init__(self, chunk_size=1028):
self.data = io.BytesIO()
self.line_break = "\r\n"
self.boundary = self._initialize_boundary()
self.chunk_size = chunk_size
def add_params(self, params):
for key, value in params.items():
if value is None:
continue
self._write(self.param_header())
self._write(self.line_break)
if hasattr(value, 'read'):
self._write("Content-Disposition: form-data; name=\"")
self._write(key)
self._write("\"; filename=\"")
self._write(value.name)
self._write("\"")
self._write(self.line_break)
self._write("Content-Type: application/octet-stream")
self._write(self.line_break)
self._write(self.line_break)
self._write_file(value)
else:
self._write("Content-Disposition: form-data; name=\"")
self._write(key)
self._write("\"")
self._write(self.line_break)
self._write(self.line_break)
self._write(value)
self._write(self.line_break)
def param_header(self):
return "--%s" % self.boundary
def get_post_data(self):
self._write("--%s--" % (self.boundary,))
self._write(self.line_break)
return self.data.getvalue()
def _write(self, value):
if sys.version_info < (3,):
binary_type = str
text_type = str
else:
binary_type = bytes
text_type = str
if isinstance(value, binary_type):
array = bytearray(value)
elif isinstance(value, text_type):
array = bytearray(value, encoding='utf-8')
else:
raise TypeError("unexpected type: {value_type}"
.format(value_type=type(value)))
self.data.write(array)
def _write_file(self, f):
while True:
file_contents = f.read(self.chunk_size)
if not file_contents:
break
self._write(file_contents)
def _initialize_boundary(self):
return random.randint(0, 2**63)
+979
View File
@@ -0,0 +1,979 @@
import urllib.request, urllib.parse, urllib.error
import warnings
import sys
if 1/2 != 0: str=str
from libraries.stripe import api_requestor, error, util, upload_api_base
def convert_to_stripe_object(resp, api_key, account):
types = {
'account': Account,
'alipay_account': AlipayAccount,
'application_fee': ApplicationFee,
'bank_account': BankAccount,
'bitcoin_receiver': BitcoinReceiver,
'bitcoin_transaction': BitcoinTransaction,
'card': Card,
'charge': Charge,
'country_spec': CountrySpec,
'coupon': Coupon,
'customer': Customer,
'dispute': Dispute,
'event': Event,
'fee_refund': ApplicationFeeRefund,
'file_upload': FileUpload,
'invoice': Invoice,
'invoiceitem': InvoiceItem,
'list': ListObject,
'plan': Plan,
'recipient': Recipient,
'refund': Refund,
'subscription': Subscription,
'three_d_secure': ThreeDSecure,
'token': Token,
'transfer': Transfer,
'transfer_reversal': Reversal,
'product': Product,
'sku': SKU,
'order': Order,
'order_return': OrderReturn
}
if isinstance(resp, list):
return [convert_to_stripe_object(i, api_key, account) for i in resp]
elif isinstance(resp, dict) and not isinstance(resp, StripeObject):
resp = resp.copy()
klass_name = resp.get('object')
if isinstance(klass_name, str):
klass = types.get(klass_name, StripeObject)
else:
klass = StripeObject
return klass.construct_from(resp, api_key, stripe_account=account)
else:
return resp
def populate_headers(idempotency_key):
if idempotency_key is not None:
return {"Idempotency-Key": idempotency_key}
return None
def _compute_diff(current, previous):
if isinstance(current, dict):
previous = previous or {}
diff = current.copy()
for key in set(previous.keys()) - set(diff.keys()):
diff[key] = ""
return diff
return current if current is not None else ""
def _serialize_list(array, previous):
array = array or []
previous = previous or []
params = {}
for i, v in enumerate(array):
previous_item = previous[i] if len(previous) > i else None
if hasattr(v, 'serialize'):
params[str(i)] = v.serialize(previous_item)
else:
params[str(i)] = _compute_diff(v, previous_item)
return params
class StripeObject(dict):
def __init__(self, id=None, api_key=None, stripe_account=None, **params):
super(StripeObject, self).__init__()
self._unsaved_values = set()
self._transient_values = set()
self._retrieve_params = params
self._previous = None
object.__setattr__(self, 'api_key', api_key)
object.__setattr__(self, 'stripe_account', stripe_account)
if id:
self['id'] = id
def update(self, update_dict):
for k in update_dict:
self._unsaved_values.add(k)
return super(StripeObject, self).update(update_dict)
def __setattr__(self, k, v):
if k[0] == '_' or k in self.__dict__:
return super(StripeObject, self).__setattr__(k, v)
else:
self[k] = v
def __getattr__(self, k):
if k[0] == '_':
raise AttributeError(k)
try:
return self[k]
except KeyError as err:
raise AttributeError(*err.args)
def __delattr__(self, k):
if k[0] == '_' or k in self.__dict__:
return super(StripeObject, self).__delattr__(k)
else:
del self[k]
def __setitem__(self, k, v):
if v == "":
raise ValueError(
"You cannot set %s to an empty string. "
"We interpret empty strings as None in requests."
"You may set %s.%s = None to delete the property" % (
k, str(self), k))
super(StripeObject, self).__setitem__(k, v)
# Allows for unpickling in Python 3.x
if not hasattr(self, '_unsaved_values'):
self._unsaved_values = set()
self._unsaved_values.add(k)
def __getitem__(self, k):
try:
return super(StripeObject, self).__getitem__(k)
except KeyError as err:
if k in self._transient_values:
raise KeyError(
"%r. HINT: The %r attribute was set in the past."
"It was then wiped when refreshing the object with "
"the result returned by Stripe's API, probably as a "
"result of a save(). The attributes currently "
"available on this object are: %s" %
(k, k, ', '.join(list(self.keys()))))
else:
raise err
def __delitem__(self, k):
super(StripeObject, self).__delitem__(k)
# Allows for unpickling in Python 3.x
if hasattr(self, '_unsaved_values'):
self._unsaved_values.remove(k)
@classmethod
def construct_from(cls, values, key, stripe_account=None):
instance = cls(values.get('id'), api_key=key,
stripe_account=stripe_account)
instance.refresh_from(values, api_key=key,
stripe_account=stripe_account)
return instance
def refresh_from(self, values, api_key=None, partial=False,
stripe_account=None):
self.api_key = api_key or getattr(values, 'api_key', None)
self.stripe_account = \
stripe_account or getattr(values, 'stripe_account', None)
# Wipe old state before setting new. This is useful for e.g.
# updating a customer, where there is no persistent card
# parameter. Mark those values which don't persist as transient
if partial:
self._unsaved_values = (self._unsaved_values - set(values))
else:
removed = set(self.keys()) - set(values)
self._transient_values = self._transient_values | removed
self._unsaved_values = set()
self.clear()
self._transient_values = self._transient_values - set(values)
for k, v in values.items():
super(StripeObject, self).__setitem__(
k, convert_to_stripe_object(v, api_key, stripe_account))
self._previous = values
@classmethod
def api_base(cls):
return None
def request(self, method, url, params=None, headers=None):
if params is None:
params = self._retrieve_params
requestor = api_requestor.APIRequestor(
key=self.api_key, api_base=self.api_base(),
account=self.stripe_account)
response, api_key = requestor.request(method, url, params, headers)
return convert_to_stripe_object(response, api_key, self.stripe_account)
def __repr__(self):
ident_parts = [type(self).__name__]
if isinstance(self.get('object'), str):
ident_parts.append(self.get('object'))
if isinstance(self.get('id'), str):
ident_parts.append('id=%s' % (self.get('id'),))
unicode_repr = '<%s at %s> JSON: %s' % (
' '.join(ident_parts), hex(id(self)), str(self))
if sys.version_info[0] < 3:
return unicode_repr.encode('utf-8')
else:
return unicode_repr
def __str__(self):
return util.json.dumps(self, sort_keys=True, indent=2)
def to_dict(self):
warnings.warn(
'The `to_dict` method is deprecated and will be removed in '
'version 2.0 of the Stripe bindings. The StripeObject is '
'itself now a subclass of `dict`.',
DeprecationWarning)
return dict(self)
@property
def stripe_id(self):
return self.id
def serialize(self, previous):
params = {}
unsaved_keys = self._unsaved_values or set()
previous = previous or self._previous or {}
for k, v in list(self.items()):
if k == 'id' or (isinstance(k, str) and k.startswith('_')):
continue
elif isinstance(v, APIResource):
continue
elif hasattr(v, 'serialize'):
params[k] = v.serialize(previous.get(k, None))
elif k in unsaved_keys:
params[k] = _compute_diff(v, previous.get(k, None))
elif k == 'additional_owners' and v is not None:
params[k] = _serialize_list(v, previous.get(k, None))
return params
class StripeObjectEncoder(util.json.JSONEncoder):
def __init__(self, *args, **kwargs):
warnings.warn(
'`StripeObjectEncoder` is deprecated and will be removed in '
'version 2.0 of the Stripe bindings. StripeObject is now a '
'subclass of `dict` and is handled natively by the built-in '
'json library.',
DeprecationWarning)
super(StripeObjectEncoder, self).__init__(*args, **kwargs)
class APIResource(StripeObject):
@classmethod
def retrieve(cls, id, api_key=None, **params):
instance = cls(id, api_key, **params)
instance.refresh()
return instance
def refresh(self):
self.refresh_from(self.request('get', self.instance_url()))
return self
@classmethod
def class_name(cls):
if cls == APIResource:
raise NotImplementedError(
'APIResource is an abstract class. You should perform '
'actions on its subclasses (e.g. Charge, Customer)')
return str(urllib.parse.quote_plus(cls.__name__.lower()))
@classmethod
def class_url(cls):
cls_name = cls.class_name()
return "/v1/%ss" % (cls_name,)
def instance_url(self):
id = self.get('id')
if not id:
raise error.InvalidRequestError(
'Could not determine which URL to request: %s instance '
'has invalid ID: %r' % (type(self).__name__, id), 'id')
id = util.utf8(id)
base = self.class_url()
extn = urllib.parse.quote_plus(id)
return "%s/%s" % (base, extn)
class ListObject(StripeObject):
def list(self, **params):
return self.request('get', self['url'], params)
def all(self, **params):
warnings.warn("The `all` method is deprecated and will"
"be removed in future versions. Please use the "
"`list` method instead",
DeprecationWarning)
return self.list(**params)
def auto_paging_iter(self):
page = self
params = dict(self._retrieve_params)
while True:
item_id = None
for item in page:
item_id = item.get('id', None)
yield item
if not getattr(page, 'has_more', False) or item_id is None:
return
params['starting_after'] = item_id
page = self.list(**params)
def create(self, idempotency_key=None, **params):
headers = populate_headers(idempotency_key)
return self.request('post', self['url'], params, headers)
def retrieve(self, id, **params):
base = self.get('url')
id = util.utf8(id)
extn = urllib.parse.quote_plus(id)
url = "%s/%s" % (base, extn)
return self.request('get', url, params)
def __iter__(self):
return getattr(self, 'data', []).__iter__()
class SingletonAPIResource(APIResource):
@classmethod
def retrieve(cls, **params):
return super(SingletonAPIResource, cls).retrieve(None, **params)
@classmethod
def class_url(cls):
cls_name = cls.class_name()
return "/v1/%s" % (cls_name,)
def instance_url(self):
return self.class_url()
# Classes of API operations
class ListableAPIResource(APIResource):
@classmethod
def all(cls, *args, **params):
warnings.warn("The `all` class method is deprecated and will"
"be removed in future versions. Please use the "
"`list` class method instead",
DeprecationWarning)
return cls.list(*args, **params)
@classmethod
def auto_paging_iter(self, *args, **params):
return self.list(*args, **params).auto_paging_iter()
@classmethod
def list(cls, api_key=None, idempotency_key=None,
stripe_account=None, **params):
requestor = api_requestor.APIRequestor(api_key,
api_base=cls.api_base(),
account=stripe_account)
url = cls.class_url()
response, api_key = requestor.request('get', url, params)
stripe_object = convert_to_stripe_object(response, api_key,
stripe_account)
stripe_object._retrieve_params = params
return stripe_object
class CreateableAPIResource(APIResource):
@classmethod
def create(cls, api_key=None, idempotency_key=None,
stripe_account=None, **params):
requestor = api_requestor.APIRequestor(api_key, account=stripe_account)
url = cls.class_url()
headers = populate_headers(idempotency_key)
response, api_key = requestor.request('post', url, params, headers)
return convert_to_stripe_object(response, api_key, stripe_account)
class UpdateableAPIResource(APIResource):
@classmethod
def _modify(cls, url, api_key=None, idempotency_key=None,
stripe_account=None, **params):
requestor = api_requestor.APIRequestor(api_key, account=stripe_account)
headers = populate_headers(idempotency_key)
response, api_key = requestor.request('post', url, params, headers)
return convert_to_stripe_object(response, api_key, stripe_account)
@classmethod
def modify(cls, sid, **params):
url = "%s/%s" % (cls.class_url(), urllib.parse.quote_plus(util.utf8(sid)))
return cls._modify(url, **params)
def save(self, idempotency_key=None):
updated_params = self.serialize(None)
headers = populate_headers(idempotency_key)
if updated_params:
self.refresh_from(self.request('post', self.instance_url(),
updated_params, headers))
else:
util.logger.debug("Trying to save already saved object %r", self)
return self
class DeletableAPIResource(APIResource):
def delete(self, **params):
self.refresh_from(self.request('delete', self.instance_url(), params))
return self
# API objects
class Account(CreateableAPIResource, ListableAPIResource,
UpdateableAPIResource, DeletableAPIResource):
@classmethod
def retrieve(cls, id=None, api_key=None, **params):
instance = cls(id, api_key, **params)
instance.refresh()
return instance
@classmethod
def modify(cls, id=None, **params):
return cls._modify(cls._build_instance_url(id), **params)
@classmethod
def _build_instance_url(cls, sid):
if not sid:
return "/v1/account"
sid = util.utf8(sid)
base = cls.class_url()
extn = urllib.parse.quote_plus(sid)
return "%s/%s" % (base, extn)
def instance_url(self):
return self._build_instance_url(self.get('id'))
def reject(self, reason=None, idempotency_key=None):
url = self.instance_url() + '/reject'
headers = populate_headers(idempotency_key)
if reason:
params = {"reason": reason}
else:
params = {}
self.refresh_from(
self.request('post', url, params, headers)
)
return self
class AlipayAccount(UpdateableAPIResource, DeletableAPIResource):
@classmethod
def _build_instance_url(cls, customer, sid):
token = util.utf8(sid)
extn = urllib.parse.quote_plus(token)
customer = util.utf8(customer)
base = Customer.class_url()
owner_extn = urllib.parse.quote_plus(customer)
return "%s/%s/sources/%s" % (base, owner_extn, extn)
def instance_url(self):
return self._build_instance_url(self.customer, self.id)
@classmethod
def modify(cls, customer, id, **params):
url = cls._build_instance_url(customer, id)
return cls._modify(url, **params)
@classmethod
def retrieve(cls, id, api_key=None, stripe_account=None, **params):
raise NotImplementedError(
"Can't retrieve an Alipay account without a customer ID. "
"Use customer.sources.retrieve('alipay_account_id') instead.")
class Balance(SingletonAPIResource):
pass
class BalanceTransaction(ListableAPIResource):
@classmethod
def class_url(cls):
return '/v1/balance/history'
class Card(UpdateableAPIResource, DeletableAPIResource):
def instance_url(self):
token = util.utf8(self.id)
extn = urllib.parse.quote_plus(token)
if (hasattr(self, 'customer')):
customer = util.utf8(self.customer)
base = Customer.class_url()
owner_extn = urllib.parse.quote_plus(customer)
class_base = "sources"
elif (hasattr(self, 'recipient')):
recipient = util.utf8(self.recipient)
base = Recipient.class_url()
owner_extn = urllib.parse.quote_plus(recipient)
class_base = "cards"
elif (hasattr(self, 'account')):
account = util.utf8(self.account)
base = Account.class_url()
owner_extn = urllib.parse.quote_plus(account)
class_base = "external_accounts"
else:
raise error.InvalidRequestError(
"Could not determine whether card_id %s is "
"attached to a customer, recipient, or "
"account." % token, 'id')
return "%s/%s/%s/%s" % (base, owner_extn, class_base, extn)
@classmethod
def modify(cls, sid, **params):
raise NotImplementedError(
"Can't modify a card without a customer, recipient or account "
"ID. Call save on customer.sources.retrieve('card_id'), "
"recipient.cards.retrieve('card_id'), or "
"account.external_accounts.retrieve('card_id') instead.")
@classmethod
def retrieve(cls, id, api_key=None, stripe_account=None, **params):
raise NotImplementedError(
"Can't retrieve a card without a customer, recipient or account "
"ID. Use customer.sources.retrieve('card_id'), "
"recipient.cards.retrieve('card_id'), or "
"account.external_accounts.retrieve('card_id') instead.")
class VerifyMixin(object):
def verify(self, idempotency_key=None, **params):
url = self.instance_url() + '/verify'
headers = populate_headers(idempotency_key)
self.refresh_from(self.request('post', url, params, headers))
return self
class BankAccount(UpdateableAPIResource, DeletableAPIResource, VerifyMixin):
def instance_url(self):
token = util.utf8(self.id)
extn = urllib.parse.quote_plus(token)
if (hasattr(self, 'customer')):
customer = util.utf8(self.customer)
base = Customer.class_url()
owner_extn = urllib.parse.quote_plus(customer)
class_base = "sources"
elif (hasattr(self, 'account')):
account = util.utf8(self.account)
base = Account.class_url()
owner_extn = urllib.parse.quote_plus(account)
class_base = "external_accounts"
else:
raise error.InvalidRequestError(
"Could not determine whether bank_account_id %s is "
"attached to a customer or an account." % token, 'id')
return "%s/%s/%s/%s" % (base, owner_extn, class_base, extn)
@classmethod
def modify(cls, sid, **params):
raise NotImplementedError(
"Can't modify a bank account without a customer or account ID. "
"Call save on customer.sources.retrieve('bank_account_id') or "
"account.external_accounts.retrieve('bank_account_id') instead.")
@classmethod
def retrieve(cls, id, api_key=None, stripe_account=None, **params):
raise NotImplementedError(
"Can't retrieve a bank account without a customer or account ID. "
"Use customer.sources.retrieve('bank_account_id') or "
"account.external_accounts.retrieve('bank_account_id') instead.")
class Charge(CreateableAPIResource, ListableAPIResource,
UpdateableAPIResource):
def refund(self, idempotency_key=None, **params):
url = self.instance_url() + '/refund'
headers = populate_headers(idempotency_key)
self.refresh_from(self.request('post', url, params, headers))
return self
def capture(self, idempotency_key=None, **params):
url = self.instance_url() + '/capture'
headers = populate_headers(idempotency_key)
self.refresh_from(self.request('post', url, params, headers))
return self
def update_dispute(self, idempotency_key=None, **params):
requestor = api_requestor.APIRequestor(self.api_key,
account=self.stripe_account)
url = self.instance_url() + '/dispute'
headers = populate_headers(idempotency_key)
response, api_key = requestor.request('post', url, params, headers)
self.refresh_from({'dispute': response}, api_key, True)
return self.dispute
def close_dispute(self, idempotency_key=None):
requestor = api_requestor.APIRequestor(self.api_key,
account=self.stripe_account)
url = self.instance_url() + '/dispute/close'
headers = populate_headers(idempotency_key)
response, api_key = requestor.request('post', url, {}, headers)
self.refresh_from({'dispute': response}, api_key, True)
return self.dispute
def mark_as_fraudulent(self, idempotency_key=None):
params = {
'fraud_details': {'user_report': 'fraudulent'}
}
url = self.instance_url()
headers = populate_headers(idempotency_key)
self.refresh_from(self.request('post', url, params, headers))
return self
def mark_as_safe(self, idempotency_key=None):
params = {
'fraud_details': {'user_report': 'safe'}
}
url = self.instance_url()
headers = populate_headers(idempotency_key)
self.refresh_from(self.request('post', url, params, headers))
return self
class Dispute(CreateableAPIResource, ListableAPIResource,
UpdateableAPIResource):
def close(self, idempotency_key=None):
url = self.instance_url() + '/close'
headers = populate_headers(idempotency_key)
self.refresh_from(self.request('post', url, {}, headers))
return self
class Customer(CreateableAPIResource, UpdateableAPIResource,
ListableAPIResource, DeletableAPIResource):
def add_invoice_item(self, idempotency_key=None, **params):
params['customer'] = self.id
ii = InvoiceItem.create(self.api_key,
idempotency_key=idempotency_key, **params)
return ii
def invoices(self, **params):
params['customer'] = self.id
invoices = Invoice.list(self.api_key, **params)
return invoices
def invoice_items(self, **params):
params['customer'] = self.id
iis = InvoiceItem.list(self.api_key, **params)
return iis
def charges(self, **params):
params['customer'] = self.id
charges = Charge.list(self.api_key, **params)
return charges
def update_subscription(self, idempotency_key=None, **params):
warnings.warn(
'The `update_subscription` method is deprecated. Instead, use the '
'`subscriptions` resource on the customer object to update a '
'subscription',
DeprecationWarning)
requestor = api_requestor.APIRequestor(self.api_key,
account=self.stripe_account)
url = self.instance_url() + '/subscription'
headers = populate_headers(idempotency_key)
response, api_key = requestor.request('post', url, params, headers)
self.refresh_from({'subscription': response}, api_key, True)
return self.subscription
def cancel_subscription(self, idempotency_key=None, **params):
warnings.warn(
'The `cancel_subscription` method is deprecated. Instead, use the '
'`subscriptions` resource on the customer object to cancel a '
'subscription',
DeprecationWarning)
requestor = api_requestor.APIRequestor(self.api_key,
account=self.stripe_account)
url = self.instance_url() + '/subscription'
headers = populate_headers(idempotency_key)
response, api_key = requestor.request('delete', url, params, headers)
self.refresh_from({'subscription': response}, api_key, True)
return self.subscription
def delete_discount(self, **params):
requestor = api_requestor.APIRequestor(self.api_key,
account=self.stripe_account)
url = self.instance_url() + '/discount'
_, api_key = requestor.request('delete', url)
self.refresh_from({'discount': None}, api_key, True)
class Invoice(CreateableAPIResource, ListableAPIResource,
UpdateableAPIResource):
def pay(self, idempotency_key=None):
headers = populate_headers(idempotency_key)
return self.request('post', self.instance_url() + '/pay', {}, headers)
@classmethod
def upcoming(cls, api_key=None, stripe_account=None, **params):
requestor = api_requestor.APIRequestor(api_key,
account=stripe_account)
url = cls.class_url() + '/upcoming'
response, api_key = requestor.request('get', url, params)
return convert_to_stripe_object(response, api_key, stripe_account)
class InvoiceItem(CreateableAPIResource, UpdateableAPIResource,
ListableAPIResource, DeletableAPIResource):
pass
class Plan(CreateableAPIResource, DeletableAPIResource,
UpdateableAPIResource, ListableAPIResource):
pass
class Subscription(CreateableAPIResource, DeletableAPIResource,
UpdateableAPIResource, ListableAPIResource):
def delete_discount(self, **params):
requestor = api_requestor.APIRequestor(self.api_key,
account=self.stripe_account)
url = self.instance_url() + '/discount'
_, api_key = requestor.request('delete', url)
self.refresh_from({'discount': None}, api_key, True)
class Refund(CreateableAPIResource, ListableAPIResource,
UpdateableAPIResource):
pass
class Token(CreateableAPIResource):
pass
class Coupon(CreateableAPIResource, UpdateableAPIResource,
DeletableAPIResource, ListableAPIResource):
pass
class Event(ListableAPIResource):
pass
class Transfer(CreateableAPIResource, UpdateableAPIResource,
ListableAPIResource):
def cancel(self):
self.refresh_from(self.request('post',
self.instance_url() + '/cancel'))
class Reversal(UpdateableAPIResource):
def instance_url(self):
token = util.utf8(self.id)
transfer = util.utf8(self.transfer)
base = Transfer.class_url()
cust_extn = urllib.parse.quote_plus(transfer)
extn = urllib.parse.quote_plus(token)
return "%s/%s/reversals/%s" % (base, cust_extn, extn)
@classmethod
def modify(cls, sid, **params):
raise NotImplementedError(
"Can't modify a reversal without a transfer"
"ID. Call save on transfer.reversals.retrieve('reversal_id')")
@classmethod
def retrieve(cls, id, api_key=None, **params):
raise NotImplementedError(
"Can't retrieve a reversal without a transfer"
"ID. Use transfer.reversals.retrieve('reversal_id')")
class Recipient(CreateableAPIResource, UpdateableAPIResource,
ListableAPIResource, DeletableAPIResource):
def transfers(self, **params):
params['recipient'] = self.id
transfers = Transfer.list(self.api_key, **params)
return transfers
class FileUpload(ListableAPIResource):
@classmethod
def api_base(cls):
return upload_api_base
@classmethod
def class_name(cls):
return 'file'
@classmethod
def create(cls, api_key=None, stripe_account=None, **params):
requestor = api_requestor.APIRequestor(
api_key, api_base=cls.api_base(), account=stripe_account)
url = cls.class_url()
supplied_headers = {
"Content-Type": "multipart/form-data"
}
response, api_key = requestor.request(
'post', url, params=params, headers=supplied_headers)
return convert_to_stripe_object(response, api_key, stripe_account)
class ApplicationFee(ListableAPIResource):
@classmethod
def class_name(cls):
return 'application_fee'
def refund(self, idempotency_key=None, **params):
headers = populate_headers(idempotency_key)
url = self.instance_url() + '/refund'
self.refresh_from(self.request('post', url, params, headers))
return self
class ApplicationFeeRefund(UpdateableAPIResource):
@classmethod
def _build_instance_url(cls, fee, sid):
fee = util.utf8(fee)
sid = util.utf8(sid)
base = ApplicationFee.class_url()
cust_extn = urllib.parse.quote_plus(fee)
extn = urllib.parse.quote_plus(sid)
return "%s/%s/refunds/%s" % (base, cust_extn, extn)
@classmethod
def modify(cls, fee, sid, **params):
url = cls._build_instance_url(fee, sid)
return cls._modify(url, **params)
def instance_url(self):
return self._build_instance_url(self.fee, self.id)
@classmethod
def retrieve(cls, id, api_key=None, **params):
raise NotImplementedError(
"Can't retrieve a refund without an application fee ID. "
"Use application_fee.refunds.retrieve('refund_id') instead.")
class BitcoinReceiver(CreateableAPIResource, UpdateableAPIResource,
DeletableAPIResource, ListableAPIResource):
def instance_url(self):
token = util.utf8(self.id)
extn = urllib.parse.quote_plus(token)
if (hasattr(self, 'customer')):
customer = util.utf8(self.customer)
base = Customer.class_url()
cust_extn = urllib.parse.quote_plus(customer)
return "%s/%s/sources/%s" % (base, cust_extn, extn)
else:
base = BitcoinReceiver.class_url()
return "%s/%s" % (base, extn)
@classmethod
def class_url(cls):
return '/v1/bitcoin/receivers'
class BitcoinTransaction(StripeObject):
pass
class Product(CreateableAPIResource, UpdateableAPIResource,
ListableAPIResource, DeletableAPIResource):
pass
class SKU(CreateableAPIResource, UpdateableAPIResource,
ListableAPIResource, DeletableAPIResource):
pass
class Order(CreateableAPIResource, UpdateableAPIResource,
ListableAPIResource):
def pay(self, idempotency_key=None, **params):
headers = populate_headers(idempotency_key)
return self.request(
'post', self.instance_url() + '/pay', params, headers)
def return_order(self, idempotency_key=None, **params):
headers = populate_headers(idempotency_key)
return self.request(
'post', self.instance_url() + '/returns', params, headers)
class OrderReturn(ListableAPIResource):
pass
@classmethod
def class_url(cls):
return '/v1/order_returns'
class CountrySpec(ListableAPIResource):
@classmethod
def class_name(cls):
return 'country_spec'
class ThreeDSecure(CreateableAPIResource):
@classmethod
def class_url(cls):
return '/v1/3d_secure'
@classmethod
def retrieve(cls, id, api_key=None, stripe_account=None, **params):
raise NotImplementedError("Can't retrieve 3D Secure objects.")
+13
View File
@@ -0,0 +1,13 @@
import os
import unittest2
def all():
path = os.path.dirname(os.path.realpath(__file__))
return unittest2.defaultTestLoader.discover(path)
def resources():
path = os.path.dirname(os.path.realpath(__file__))
return unittest2.defaultTestLoader.discover(
os.path.join(path, "resources"))
+240
View File
@@ -0,0 +1,240 @@
import datetime
import os
import random
import re
import string
import sys
import unittest2
if 1/2 != 0: str=str
from mock import patch, Mock
import libraries.stripe
NOW = datetime.datetime.now()
DUMMY_CARD = {
'number': '4242424242424242',
'exp_month': NOW.month,
'exp_year': NOW.year + 4
}
DUMMY_DEBIT_CARD = {
'number': '4000056655665556',
'exp_month': NOW.month,
'exp_year': NOW.year + 4
}
DUMMY_CHARGE = {
'amount': 100,
'currency': 'usd',
'card': DUMMY_CARD
}
DUMMY_DISPUTE = {
'status': 'needs_response',
'currency': 'usd',
'metadata': {}
}
DUMMY_PLAN = {
'amount': 2000,
'interval': 'month',
'name': 'Amazing Gold Plan',
'currency': 'usd',
'id': ('stripe-test-gold-' +
''.join(random.choice(string.ascii_lowercase) for x in range(10)))
}
DUMMY_COUPON = {
'percent_off': 25,
'duration': 'repeating',
'duration_in_months': 5,
'metadata': {}
}
DUMMY_RECIPIENT = {
'name': 'John Doe',
'type': 'individual'
}
DUMMY_TRANSFER = {
'amount': 400,
'currency': 'usd',
'recipient': 'self'
}
DUMMY_INVOICE_ITEM = {
'amount': 456,
'currency': 'usd',
}
SAMPLE_INVOICE = libraries.stripe.util.json.loads("""
{
"amount_due": 1305,
"attempt_count": 0,
"attempted": true,
"charge": "ch_wajkQ5aDTzFs5v",
"closed": true,
"customer": "cus_osllUe2f1BzrRT",
"date": 1338238728,
"discount": null,
"ending_balance": 0,
"id": "in_t9mHb2hpK7mml1",
"livemode": false,
"next_payment_attempt": null,
"object": "invoice",
"paid": true,
"period_end": 1338238728,
"period_start": 1338238716,
"starting_balance": -8695,
"subtotal": 10000,
"total": 10000,
"lines": {
"invoiceitems": [],
"prorations": [],
"subscriptions": [
{
"plan": {
"interval": "month",
"object": "plan",
"identifier": "expensive",
"currency": "usd",
"livemode": false,
"amount": 10000,
"name": "Expensive Plan",
"trial_period_days": null,
"id": "expensive"
},
"period": {
"end": 1340917128,
"start": 1338238728
},
"amount": 10000
}
]
}
}
""")
class StripeTestCase(unittest2.TestCase):
RESTORE_ATTRIBUTES = ('api_version', 'api_key')
def setUp(self):
super(StripeTestCase, self).setUp()
self._stripe_original_attributes = {}
for attr in self.RESTORE_ATTRIBUTES:
self._stripe_original_attributes[attr] = getattr(stripe, attr)
api_base = os.environ.get('STRIPE_API_BASE')
if api_base:
libraries.stripe.api_base = api_base
libraries.stripe.api_key = os.environ.get(
'STRIPE_API_KEY', 'tGN0bIwXnHdwOa85VABjPdSn8nWY7G7I')
def tearDown(self):
super(StripeTestCase, self).tearDown()
for attr in self.RESTORE_ATTRIBUTES:
setattr(stripe, attr, self._stripe_original_attributes[attr])
# Python < 2.7 compatibility
def assertRaisesRegexp(self, exception, regexp, callable, *args, **kwargs):
try:
callable(*args, **kwargs)
except exception as err:
if regexp is None:
return True
if isinstance(regexp, str):
regexp = re.compile(regexp)
if not regexp.search(str(err)):
raise self.failureException('"%s" does not match "%s"' %
(regexp.pattern, str(err)))
else:
raise self.failureException(
'%s was not raised' % (exception.__name__,))
class StripeUnitTestCase(StripeTestCase):
REQUEST_LIBRARIES = ['urlfetch', 'requests', 'pycurl']
if sys.version_info >= (3, 0):
REQUEST_LIBRARIES.append('urllib.request')
else:
REQUEST_LIBRARIES.append('urllib2')
def setUp(self):
super(StripeUnitTestCase, self).setUp()
self.request_patchers = {}
self.request_mocks = {}
for lib in self.REQUEST_LIBRARIES:
patcher = patch("stripe.http_client.%s" % (lib,))
self.request_mocks[lib] = patcher.start()
self.request_patchers[lib] = patcher
def tearDown(self):
super(StripeUnitTestCase, self).tearDown()
for patcher in self.request_patchers.values():
patcher.stop()
class StripeApiTestCase(StripeTestCase):
def setUp(self):
super(StripeApiTestCase, self).setUp()
self.requestor_patcher = patch('stripe.api_requestor.APIRequestor')
requestor_class_mock = self.requestor_patcher.start()
self.requestor_mock = requestor_class_mock.return_value
def tearDown(self):
super(StripeApiTestCase, self).tearDown()
self.requestor_patcher.stop()
def mock_response(self, res):
self.requestor_mock.request = Mock(return_value=(res, 'reskey'))
class StripeResourceTest(StripeApiTestCase):
def setUp(self):
super(StripeResourceTest, self).setUp()
self.mock_response({})
class MyResource(stripe.resource.APIResource):
pass
class MySingleton(stripe.resource.SingletonAPIResource):
pass
class MyListable(stripe.resource.ListableAPIResource):
pass
class MyCreatable(stripe.resource.CreateableAPIResource):
pass
class MyUpdateable(stripe.resource.UpdateableAPIResource):
pass
class MyDeletable(stripe.resource.DeletableAPIResource):
pass
class MyComposite(stripe.resource.ListableAPIResource,
libraries.stripe.resource.CreateableAPIResource,
libraries.stripe.resource.UpdateableAPIResource,
libraries.stripe.resource.DeletableAPIResource):
pass
View File
+154
View File
@@ -0,0 +1,154 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class AccountTest(StripeResourceTest):
def test_retrieve_account_deprecated(self):
libraries.stripe.Account.retrieve()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/account',
{},
None
)
def test_retrieve_account(self):
libraries.stripe.Account.retrieve('acct_foo')
self.requestor_mock.request.assert_called_with(
'get',
'/v1/accounts/acct_foo',
{},
None
)
def test_list_accounts(self):
libraries.stripe.Account.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/accounts',
{}
)
def test_create_account(self):
pii = {
'type': 'individual',
'first_name': 'Joe',
'last_name': 'Smith',
}
libraries.stripe.Account.create(legal_entity=pii)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/accounts',
{
'legal_entity': pii,
},
None,
)
def test_update_account(self):
acct = libraries.stripe.Account.construct_from({
'id': 'acct_update',
'legal_entity': {'first_name': 'Joe'},
}, 'api_key')
acct.legal_entity['first_name'] = 'Bob'
acct.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/accounts/acct_update',
{
'legal_entity': {
'first_name': 'Bob',
},
},
None,
)
def test_account_delete_bank_account(self):
source = libraries.stripe.BankAccount.construct_from({
'account': 'acc_delete_ba',
'id': 'ba_delete_ba',
}, 'api_key')
source.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/accounts/acc_delete_ba/external_accounts/ba_delete_ba',
{},
None
)
def test_reject_account(self):
self.mock_response({
'id': 'acct_reject',
'verification': {
'disabled_reason': 'rejected.fraud'
},
})
obj = libraries.stripe.Account.construct_from({
'id': 'acct_reject'
}, 'mykey')
self.assertTrue(obj is obj.reject(reason='fraud'))
self.assertEqual('rejected.fraud', obj.verification['disabled_reason'])
self.assertEqual('acct_reject', obj.id)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/accounts/acct_reject/reject',
{'reason': 'fraud'},
None
)
def test_reject_account_without_reason(self):
self.mock_response({
'id': 'acct_reject',
'verification': {
'disabled_reason': 'rejected.fraud'
},
})
obj = libraries.stripe.Account.construct_from({
'id': 'acct_reject'
}, 'mykey')
self.assertTrue(obj is obj.reject())
self.assertEqual('rejected.fraud', obj.verification['disabled_reason'])
self.assertEqual('acct_reject', obj.id)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/accounts/acct_reject/reject',
{},
None
)
def test_verify_additional_owner(self):
acct = libraries.stripe.Account.construct_from({
'id': 'acct_update',
'additional_owners': [{
'first_name': 'Alice',
'verification': {},
}]
}, 'api_key')
owner = acct.additional_owners[0]
owner.verification.document = 'file_foo'
acct.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/accounts/acct_update',
{
'additional_owners': {
'0': {
'verification': {
'document': 'file_foo',
},
},
},
},
None,
)
+86
View File
@@ -0,0 +1,86 @@
import libraries.stripe
from libraries.stripe.test.helper import (
StripeApiTestCase, MyResource, MySingleton
)
class APIResourceTests(StripeApiTestCase):
def test_retrieve_and_refresh(self):
self.mock_response({
'id': 'foo2',
'bobble': 'scrobble',
})
res = MyResource.retrieve('foo*', myparam=5)
url = '/v1/myresources/foo%2A'
self.requestor_mock.request.assert_called_with(
'get', url, {'myparam': 5}, None
)
self.assertEqual('scrobble', res.bobble)
self.assertEqual('foo2', res.id)
self.assertEqual('reskey', res.api_key)
self.mock_response({
'frobble': 5,
})
res = res.refresh()
url = '/v1/myresources/foo2'
self.requestor_mock.request.assert_called_with(
'get', url, {'myparam': 5}, None
)
self.assertEqual(5, res.frobble)
self.assertRaises(KeyError, res.__getitem__, 'bobble')
def test_convert_to_stripe_object(self):
sample = {
'foo': 'bar',
'adict': {
'object': 'charge',
'id': 42,
'amount': 7,
},
'alist': [
{
'object': 'customer',
'name': 'chilango'
}
]
}
converted = libraries.stripe.resource.convert_to_stripe_object(
sample, 'akey', None)
# Types
self.assertTrue(isinstance(converted, libraries.stripe.resource.StripeObject))
self.assertTrue(isinstance(converted.adict, libraries.stripe.Charge))
self.assertEqual(1, len(converted.alist))
self.assertTrue(isinstance(converted.alist[0], libraries.stripe.Customer))
# Values
self.assertEqual('bar', converted.foo)
self.assertEqual(42, converted.adict.id)
self.assertEqual('chilango', converted.alist[0].name)
# Stripping
# TODO: We should probably be stripping out this property
# self.assertRaises(AttributeError, getattr, converted.adict, 'object')
class SingletonAPIResourceTests(StripeApiTestCase):
def test_retrieve(self):
self.mock_response({
'single': 'ton'
})
res = MySingleton.retrieve()
self.requestor_mock.request.assert_called_with(
'get', '/v1/mysingleton', {}, None)
self.assertEqual('ton', res.single)
+113
View File
@@ -0,0 +1,113 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class ApplicationFeeTest(StripeResourceTest):
def test_list_application_fees(self):
libraries.stripe.ApplicationFee.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/application_fees',
{}
)
class ApplicationFeeRefundTest(StripeResourceTest):
def test_fetch_refund(self):
fee = libraries.stripe.ApplicationFee.construct_from({
'id': 'fee_get_refund',
'refunds': {
'object': 'list',
'url': '/v1/application_fees/fee_get_refund/refunds',
}
}, 'api_key')
fee.refunds.retrieve("ref_get")
self.requestor_mock.request.assert_called_with(
'get',
'/v1/application_fees/fee_get_refund/refunds/ref_get',
{},
None
)
def test_list_refunds(self):
fee = libraries.stripe.ApplicationFee.construct_from({
'id': 'fee_get_refund',
'refunds': {
'object': 'list',
'url': '/v1/application_fees/fee_get_refund/refunds',
}
}, 'api_key')
fee.refunds.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/application_fees/fee_get_refund/refunds',
{},
None
)
def test_update_refund(self):
def side_effect(*args):
raise libraries.stripe.InvalidRequestError('invalid', 'foo')
self.requestor_mock.request.side_effect = side_effect
refund = libraries.stripe.resource.ApplicationFeeRefund.construct_from({
'id': "ref_update",
'fee': "fee_update",
'metadata': {},
}, 'api_key')
refund.metadata["key"] = "foo"
try:
refund.save()
except libraries.stripe.InvalidRequestError:
pass
self.requestor_mock.request.assert_called_with(
'post',
'/v1/application_fees/fee_update/refunds/ref_update',
{
'metadata': {
'key': 'foo',
}
},
None
)
self.requestor_mock.request.side_effect = None
refund.metadata["key"] = "bar"
refund.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/application_fees/fee_update/refunds/ref_update',
{
'metadata': {
'key': 'bar',
}
},
None
)
def test_modify_refund(self):
libraries.stripe.resource.ApplicationFeeRefund.modify("fee_update", "ref_update",
metadata={'key': 'foo'},
api_key='api_key')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/application_fees/fee_update/refunds/ref_update',
{
'metadata': {
'key': 'foo',
}
},
None
)
+26
View File
@@ -0,0 +1,26 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class BalanceTest(StripeResourceTest):
def test_retrieve_balance(self):
libraries.stripe.Balance.retrieve()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/balance',
{},
None
)
class BalanceTransactionTest(StripeResourceTest):
def test_list_balance_transactions(self):
libraries.stripe.BalanceTransaction.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/balance/history',
{}
)
+111
View File
@@ -0,0 +1,111 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class BitcoinReceiverTest(StripeResourceTest):
def test_retrieve_resource(self):
libraries.stripe.BitcoinReceiver.retrieve("btcrcv_test_receiver")
self.requestor_mock.request.assert_called_with(
'get',
'/v1/bitcoin/receivers/btcrcv_test_receiver',
{},
None
)
def test_list_receivers(self):
libraries.stripe.BitcoinReceiver.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/bitcoin/receivers',
{},
)
def test_create_receiver(self):
libraries.stripe.BitcoinReceiver.create(amount=100, description="some details",
currency="usd",
email="do+fill_now@stripe.com")
self.requestor_mock.request.assert_called_with(
'post',
'/v1/bitcoin/receivers',
{
'amount': 100,
'description': 'some details',
'currency': 'usd',
'email': 'do+fill_now@stripe.com'
},
None
)
def test_update_receiver_without_customer(self):
params = {'id': 'receiver', 'amount': 100,
'description': "some details", 'currency': "usd"}
r = libraries.stripe.BitcoinReceiver.construct_from(params, 'api_key')
r.description = "some other details"
r.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/bitcoin/receivers/receiver',
{
'description': 'some other details',
},
None
)
def test_update_receiver_with_customer(self):
params = {'id': 'receiver', 'amount': 100,
'description': "some details", 'currency': "usd",
'customer': "cust"}
r = libraries.stripe.BitcoinReceiver.construct_from(params, 'api_key')
r.description = "some other details"
r.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cust/sources/receiver',
{
'description': 'some other details',
},
None
)
def test_delete_receiver_without_customer(self):
params = {'id': 'receiver', 'amount': 100,
'description': "some details", 'currency': "usd"}
r = libraries.stripe.BitcoinReceiver.construct_from(params, 'api_key')
r.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/bitcoin/receivers/receiver',
{},
None
)
def test_delete_receiver_with_customer(self):
params = {'id': 'receiver', 'amount': 100,
'description': "some details", 'currency': "usd",
'customer': "cust"}
r = libraries.stripe.BitcoinReceiver.construct_from(params, 'api_key')
r.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/customers/cust/sources/receiver',
{},
None
)
def test_list_transactions(self):
receiver = libraries.stripe.BitcoinReceiver.construct_from({
'id': 'btcrcv_foo',
'transactions': {
'object': 'list',
'url': '/v1/bitcoin/receivers/btcrcv_foo/transactions',
}
}, 'api_key')
receiver.transactions.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/bitcoin/receivers/btcrcv_foo/transactions',
{},
None
)
+113
View File
@@ -0,0 +1,113 @@
import libraries.stripe
from libraries.stripe.test.helper import (
StripeResourceTest, NOW, DUMMY_CHARGE
)
class ChargeTest(StripeResourceTest):
def test_charge_list(self):
libraries.stripe.Charge.list(created={'lt': NOW})
self.requestor_mock.request.assert_called_with(
'get',
'/v1/charges',
{
'created': {'lt': NOW},
}
)
def test_charge_create(self):
libraries.stripe.Charge.create(idempotency_key='foo', **DUMMY_CHARGE)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges',
DUMMY_CHARGE,
{'Idempotency-Key': 'foo'},
)
def test_charge_retrieve(self):
libraries.stripe.Charge.retrieve('ch_test_id')
self.requestor_mock.request.assert_called_with(
'get',
'/v1/charges/ch_test_id',
{},
None
)
def test_charge_modify(self):
libraries.stripe.Charge.modify('ch_test_id', refund=True)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_test_id',
{
'refund': True,
},
None
)
def test_charge_update_dispute(self):
charge = libraries.stripe.Charge(id='ch_update_id')
charge.update_dispute(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_update_id/dispute',
{},
{'Idempotency-Key': 'foo'},
)
def test_charge_close_dispute(self):
charge = libraries.stripe.Charge(id='ch_update_id')
charge.close_dispute(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_update_id/dispute/close',
{},
{'Idempotency-Key': 'foo'},
)
def test_mark_as_fraudulent(self):
charge = libraries.stripe.Charge(id='ch_update_id')
charge.mark_as_fraudulent(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_update_id',
{
'fraud_details': {'user_report': 'fraudulent'}
},
{'Idempotency-Key': 'foo'},
)
def test_mark_as_safe(self):
charge = libraries.stripe.Charge(id='ch_update_id')
charge.mark_as_safe(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_update_id',
{
'fraud_details': {'user_report': 'safe'}
},
{'Idempotency-Key': 'foo'},
)
def test_create_with_source_param(self):
libraries.stripe.Charge.create(amount=100, currency='usd',
source='btcrcv_test_receiver')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges',
{
'amount': 100,
'currency': 'usd',
'source': 'btcrcv_test_receiver'
},
None,
)
+23
View File
@@ -0,0 +1,23 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class CountrySpecTest(StripeResourceTest):
def test_country_spec_list(self):
libraries.stripe.CountrySpec.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/country_specs',
{}
)
def test_country_spec_retrieve(self):
libraries.stripe.CountrySpec.retrieve('US')
self.requestor_mock.request.assert_called_with(
'get',
'/v1/country_specs/US',
{},
None
)
+55
View File
@@ -0,0 +1,55 @@
import libraries.stripe
from libraries.stripe.test.helper import (
StripeResourceTest, DUMMY_COUPON
)
class CouponTest(StripeResourceTest):
def test_create_coupon(self):
libraries.stripe.Coupon.create(**DUMMY_COUPON)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/coupons',
DUMMY_COUPON,
None
)
def test_update_coupon(self):
coup = libraries.stripe.Coupon.construct_from({
'id': 'cu_update',
'metadata': {},
}, 'api_key')
coup.metadata["key"] = "value"
coup.save()
self.requestor_mock.request.assert_called_with(
'post',
"/v1/coupons/cu_update",
{
'metadata': {
'key': 'value',
}
},
None
)
def test_delete_coupon(self):
c = libraries.stripe.Coupon(id='cu_delete')
c.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/coupons/cu_delete',
{},
None
)
def test_detach_coupon(self):
customer = libraries.stripe.Customer(id="cus_delete_discount")
customer.delete_discount()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/customers/cus_delete_discount/discount',
)
+35
View File
@@ -0,0 +1,35 @@
import libraries.stripe
from libraries.stripe.test.helper import (
StripeApiTestCase, MyCreatable
)
class CreateableAPIResourceTests(StripeApiTestCase):
def test_create(self):
self.mock_response({
'object': 'charge',
'foo': 'bar',
})
res = MyCreatable.create()
self.requestor_mock.request.assert_called_with(
'post', '/v1/mycreatables', {}, None)
self.assertTrue(isinstance(res, libraries.stripe.Charge))
self.assertEqual('bar', res.foo)
def test_idempotent_create(self):
self.mock_response({
'object': 'charge',
'foo': 'bar',
})
res = MyCreatable.create(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post', '/v1/mycreatables', {}, {'Idempotency-Key': 'foo'})
self.assertTrue(isinstance(res, libraries.stripe.Charge))
self.assertEqual('bar', res.foo)
+370
View File
@@ -0,0 +1,370 @@
import datetime
import time
import warnings
import libraries.stripe
from libraries.stripe.test.helper import (
StripeResourceTest, DUMMY_CARD, DUMMY_PLAN
)
class CustomerTest(StripeResourceTest):
def test_list_customers(self):
libraries.stripe.Customer.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/customers',
{},
)
def test_create_customer(self):
libraries.stripe.Customer.create(description="foo bar", card=DUMMY_CARD,
coupon='cu_discount', idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers',
{
'coupon': 'cu_discount',
'description': 'foo bar',
'card': DUMMY_CARD
},
{'Idempotency-Key': 'foo'}
)
def test_unset_description(self):
customer = libraries.stripe.Customer(id="cus_unset_desc")
customer.description = "Hey"
customer.save(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cus_unset_desc',
{
'description': 'Hey',
},
{'Idempotency-Key': 'foo'}
)
def test_del_coupon(self):
customer = libraries.stripe.Customer(id="cus_unset_desc")
customer.description = "bar"
customer.coupon = "foo"
del customer.coupon
customer.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cus_unset_desc',
{
'description': 'bar'
},
None
)
def test_cannot_set_empty_string(self):
customer = libraries.stripe.Customer()
self.assertRaises(ValueError, setattr, customer, "description", "")
def test_customer_add_card(self):
customer = libraries.stripe.Customer.construct_from({
'id': 'cus_add_card',
'sources': {
'object': 'list',
'url': '/v1/customers/cus_add_card/sources',
},
}, 'api_key')
customer.sources.create(card=DUMMY_CARD, idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cus_add_card/sources',
{
'card': DUMMY_CARD,
},
{'Idempotency-Key': 'foo'}
)
def test_customer_add_source(self):
customer = libraries.stripe.Customer.construct_from({
'id': 'cus_add_source',
'sources': {
'object': 'list',
'url': '/v1/customers/cus_add_source/sources',
},
}, 'api_key')
customer.sources.create(source=DUMMY_CARD, idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cus_add_source/sources',
{
'source': DUMMY_CARD,
},
{'Idempotency-Key': 'foo'}
)
def test_customer_update_card(self):
card = libraries.stripe.Card.construct_from({
'customer': 'cus_update_card',
'id': 'ca_update_card',
}, 'api_key')
card.name = 'The Best'
card.save(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cus_update_card/sources/ca_update_card',
{
'name': 'The Best',
},
{'Idempotency-Key': 'foo'}
)
def test_customer_update_source(self):
source = libraries.stripe.BitcoinReceiver.construct_from({
'customer': 'cus_update_source',
'id': 'btcrcv_update_source',
}, 'api_key')
source.name = 'The Best'
source.save(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cus_update_source/sources/btcrcv_update_source',
{
'name': 'The Best',
},
{'Idempotency-Key': 'foo'}
)
def test_customer_update_alipay_account(self):
aa = libraries.stripe.AlipayAccount.construct_from({
'customer': 'cus_update_alipay',
'id': 'ali_update',
}, 'api_key')
aa.metadata = {'name': 'The Best'}
aa.save(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cus_update_alipay/sources/ali_update',
{
'metadata': {'name': 'The Best'},
},
{'Idempotency-Key': 'foo'}
)
def test_customer_delete_card(self):
card = libraries.stripe.Card.construct_from({
'customer': 'cus_delete_card',
'id': 'ca_delete_card',
}, 'api_key')
card.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/customers/cus_delete_card/sources/ca_delete_card',
{},
None
)
def test_customer_delete_source(self):
source = libraries.stripe.BitcoinReceiver.construct_from({
'customer': 'cus_delete_source',
'id': 'btcrcv_delete_source',
}, 'api_key')
source.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/customers/cus_delete_source/sources/btcrcv_delete_source',
{},
None
)
def test_customer_delete_alipay_account(self):
aa = libraries.stripe.AlipayAccount.construct_from({
'customer': 'cus_delete_alipay',
'id': 'ali_delete',
}, 'api_key')
aa.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/customers/cus_delete_alipay/sources/ali_delete',
{},
None
)
def test_customer_delete_bank_account(self):
source = libraries.stripe.BankAccount.construct_from({
'customer': 'cus_delete_source',
'id': 'ba_delete_source',
}, 'api_key')
source.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/customers/cus_delete_source/sources/ba_delete_source',
{},
None
)
def test_customer_verify_bank_account(self):
source = libraries.stripe.BankAccount.construct_from({
'customer': 'cus_verify_source',
'id': 'ba_verify_source',
}, 'api_key')
source.verify()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cus_verify_source/sources/ba_verify_source/verify',
{},
None
)
class CustomerPlanTest(StripeResourceTest):
def test_create_customer(self):
libraries.stripe.Customer.create(plan=DUMMY_PLAN['id'], card=DUMMY_CARD)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers',
{
'card': DUMMY_CARD,
'plan': DUMMY_PLAN['id'],
},
None
)
def test_legacy_update_subscription(self):
with warnings.catch_warnings(record=True) as w:
customer = libraries.stripe.Customer(id="cus_legacy_sub_update")
customer.update_subscription(idempotency_key='foo',
plan=DUMMY_PLAN['id'])
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cus_legacy_sub_update/subscription',
{
'plan': DUMMY_PLAN['id'],
},
{'Idempotency-Key': 'foo'}
)
self.assertEqual(len(w), 1)
self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
def test_legacy_delete_subscription(self):
with warnings.catch_warnings(record=True) as w:
customer = libraries.stripe.Customer(id="cus_legacy_sub_delete")
customer.cancel_subscription()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/customers/cus_legacy_sub_delete/subscription',
{},
None
)
self.assertEqual(len(w), 1)
self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
def test_list_customer_subscriptions(self):
customer = libraries.stripe.Customer.construct_from({
'id': 'cus_foo',
'subscriptions': {
'object': 'list',
'url': 'v1/customers/cus_foo/subscriptions',
}
}, 'api_key')
customer.subscriptions.all()
self.requestor_mock.request.assert_called_with(
'get',
'v1/customers/cus_foo/subscriptions',
{},
None
)
def test_create_customer_subscription(self):
customer = libraries.stripe.Customer.construct_from({
'id': 'cus_sub_create',
'subscriptions': {
'object': 'list',
'url': '/v1/customers/cus_sub_create/subscriptions',
}
}, 'api_key')
customer.subscriptions.create(plan=DUMMY_PLAN['id'], coupon='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/customers/cus_sub_create/subscriptions',
{
'plan': DUMMY_PLAN['id'],
'coupon': 'foo',
},
None
)
def test_retrieve_customer_subscription(self):
customer = libraries.stripe.Customer.construct_from({
'id': 'cus_foo',
'subscriptions': {
'object': 'list',
'url': '/v1/customers/cus_foo/subscriptions',
}
}, 'api_key')
customer.subscriptions.retrieve('sub_cus')
self.requestor_mock.request.assert_called_with(
'get',
'/v1/customers/cus_foo/subscriptions/sub_cus',
{},
None
)
def test_update_customer_subscription(self):
subscription = libraries.stripe.Subscription.construct_from({
'id': "sub_update",
'customer': "cus_foo",
}, 'api_key')
trial_end_dttm = datetime.datetime.now() + datetime.timedelta(days=15)
trial_end_int = int(time.mktime(trial_end_dttm.timetuple()))
subscription.trial_end = trial_end_int
subscription.plan = DUMMY_PLAN['id']
subscription.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/subscriptions/sub_update',
{
'plan': DUMMY_PLAN['id'],
'trial_end': trial_end_int,
},
None
)
def test_delete_customer_subscription(self):
subscription = libraries.stripe.Subscription.construct_from({
'id': "sub_delete",
'customer': "cus_foo",
}, 'api_key')
subscription.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/subscriptions/sub_delete',
{},
None
)
+21
View File
@@ -0,0 +1,21 @@
from libraries.stripe.test.helper import (
StripeApiTestCase, MyDeletable
)
class DeletableAPIResourceTests(StripeApiTestCase):
def test_delete(self):
self.mock_response({
'id': 'mid',
'deleted': True,
})
obj = MyDeletable.construct_from({
'id': 'mid'
}, 'mykey')
self.assertTrue(obj is obj.delete())
self.assertEqual(True, obj.deleted)
self.assertEqual('mid', obj.id)
+70
View File
@@ -0,0 +1,70 @@
import libraries.stripe
from libraries.stripe.test.helper import (
StripeResourceTest, DUMMY_DISPUTE, NOW
)
class DisputeTest(StripeResourceTest):
def test_list_all_disputes(self):
libraries.stripe.Dispute.list(created={'lt': NOW})
self.requestor_mock.request.assert_called_with(
'get',
'/v1/disputes',
{
'created': {'lt': NOW},
}
)
def test_create_dispute(self):
libraries.stripe.Dispute.create(idempotency_key='foo', **DUMMY_DISPUTE)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/disputes',
DUMMY_DISPUTE,
{'Idempotency-Key': 'foo'},
)
def test_retrieve_dispute(self):
libraries.stripe.Dispute.retrieve('dp_test_id')
self.requestor_mock.request.assert_called_with(
'get',
'/v1/disputes/dp_test_id',
{},
None
)
def test_update_dispute(self):
dispute = libraries.stripe.Dispute.construct_from({
'id': 'dp_update_id',
'evidence': {
'product_description': 'description',
},
}, 'api_key')
dispute.evidence['customer_name'] = 'customer'
dispute.evidence['uncategorized_text'] = 'text'
dispute.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/disputes/dp_update_id',
{'evidence': {
'customer_name': 'customer',
'uncategorized_text': 'text',
}},
None
)
def test_close_dispute(self):
dispute = libraries.stripe.Dispute(id='dp_close_id')
dispute.close(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/disputes/dp_close_id/close',
{},
{'Idempotency-Key': 'foo'},
)
+40
View File
@@ -0,0 +1,40 @@
import tempfile
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class FileUploadTest(StripeResourceTest):
def test_create_file_upload(self):
test_file = tempfile.TemporaryFile()
libraries.stripe.FileUpload.create(
purpose='dispute_evidence',
file=test_file
)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/files',
params={
'purpose': 'dispute_evidence',
'file': test_file
},
headers={'Content-Type': 'multipart/form-data'}
)
def test_fetch_file_upload(self):
libraries.stripe.FileUpload.retrieve("fil_foo")
self.requestor_mock.request.assert_called_with(
'get',
'/v1/files/fil_foo',
{},
None
)
def test_list_file_uploads(self):
libraries.stripe.FileUpload.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/files',
{}
)
+76
View File
@@ -0,0 +1,76 @@
import libraries.stripe
from libraries.stripe.test.helper import (
StripeResourceTest, DUMMY_INVOICE_ITEM
)
class InvoiceTest(StripeResourceTest):
def test_add_invoice_item(self):
customer = libraries.stripe.Customer(id="cus_invoice_items")
customer.add_invoice_item(**DUMMY_INVOICE_ITEM)
expected = DUMMY_INVOICE_ITEM.copy()
expected['customer'] = 'cus_invoice_items'
self.requestor_mock.request.assert_called_with(
'post',
'/v1/invoiceitems',
expected,
None,
)
def test_retrieve_invoice_items(self):
customer = libraries.stripe.Customer(id="cus_get_invoice_items")
customer.invoice_items()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/invoiceitems',
{'customer': 'cus_get_invoice_items'},
)
def test_invoice_create(self):
customer = libraries.stripe.Customer(id="cus_invoice")
libraries.stripe.Invoice.create(customer=customer.id)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/invoices',
{
'customer': 'cus_invoice',
},
None
)
def test_retrieve_customer_invoices(self):
customer = libraries.stripe.Customer(id="cus_invoice_items")
customer.invoices()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/invoices',
{
'customer': 'cus_invoice_items',
},
)
def test_pay_invoice(self):
invoice = libraries.stripe.Invoice(id="ii_pay")
invoice.pay()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/invoices/ii_pay/pay',
{},
None
)
def test_upcoming_invoice(self):
libraries.stripe.Invoice.upcoming()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/invoices/upcoming',
{},
)
+123
View File
@@ -0,0 +1,123 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeApiTestCase
class ListObjectTests(StripeApiTestCase):
def setUp(self):
super(ListObjectTests, self).setUp()
self.lo = libraries.stripe.resource.ListObject.construct_from({
'id': 'me',
'url': '/my/path',
'data': ['foo'],
}, 'mykey')
self.mock_response([{
'object': 'charge',
'foo': 'bar',
}])
def assertResponse(self, res):
self.assertTrue(isinstance(res[0], libraries.stripe.Charge))
self.assertEqual('bar', res[0].foo)
def test_for_loop(self):
seen = []
for item in self.lo:
seen.append(item)
self.assertEqual(['foo'], seen)
def test_list(self):
res = self.lo.list(myparam='you')
self.requestor_mock.request.assert_called_with(
'get', '/my/path', {'myparam': 'you'}, None)
self.assertResponse(res)
def test_create(self):
res = self.lo.create(myparam='eter')
self.requestor_mock.request.assert_called_with(
'post', '/my/path', {'myparam': 'eter'}, None)
self.assertResponse(res)
def test_retrieve(self):
res = self.lo.retrieve('myid', myparam='cow')
self.requestor_mock.request.assert_called_with(
'get', '/my/path/myid', {'myparam': 'cow'}, None)
self.assertResponse(res)
class AutoPagingTests(StripeApiTestCase):
@staticmethod
def pageable_model_response(ids, has_more):
return {
'object': 'list',
'url': '/v1/pageablemodels',
'data': [{'id': id, 'object': 'pageablemodel'} for id in ids],
'has_more': has_more,
}
def test_iter_one_page(self):
lo = libraries.stripe.resource.ListObject.construct_from(
self.pageable_model_response(['pm_123', 'pm_124'], False),
'mykey'
)
self.requestor_mock.request.assert_not_called()
seen = [item['id'] for item in lo.auto_paging_iter()]
self.assertEqual(['pm_123', 'pm_124'], seen)
def test_iter_two_pages(self):
lo = libraries.stripe.resource.ListObject.construct_from(
self.pageable_model_response(['pm_123', 'pm_124'], True),
'mykey'
)
lo._retrieve_params = {'foo': 'bar'}
self.mock_response(
self.pageable_model_response(['pm_125', 'pm_126'], False)
)
seen = [item['id'] for item in lo.auto_paging_iter()]
self.requestor_mock.request.assert_called_with(
'get',
'/v1/pageablemodels',
{
'starting_after': 'pm_124',
'foo': 'bar'
},
None
)
self.assertEqual(['pm_123', 'pm_124', 'pm_125', 'pm_126'], seen)
def test_class_method_two_pages(self):
self.mock_response({
'object': 'list',
'data': [{'id': 'ch_001'}],
'url': '/v1/charges',
'has_more': False,
})
seen = [item['id'] for item in libraries.stripe.Charge.auto_paging_iter(
limit=25,
foo='bar'
)]
self.requestor_mock.request.assert_called_with(
'get', '/v1/charges', {'limit': 25, 'foo': 'bar'}
)
self.assertEqual(['ch_001'], seen)
+35
View File
@@ -0,0 +1,35 @@
import libraries.stripe
from libraries.stripe.test.helper import (
StripeApiTestCase, MyListable
)
class ListableAPIResourceTests(StripeApiTestCase):
def test_all(self):
self.mock_response({
'object': 'list',
'data': [
{
'object': 'charge',
'name': 'jose',
},
{
'object': 'charge',
'name': 'curly',
}
],
'url': '/v1/charges',
'has_more': False,
})
res = MyListable.list()
self.requestor_mock.request.assert_called_with(
'get', '/v1/mylistables', {})
self.assertEqual(2, len(res.data))
self.assertTrue(all(isinstance(obj, libraries.stripe.Charge)
for obj in res.data))
self.assertEqual('jose', res.data[0].name)
self.assertEqual('curly', res.data[1].name)
+61
View File
@@ -0,0 +1,61 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class MetadataTest(StripeResourceTest):
def test_noop_metadata(self):
charge = libraries.stripe.Charge(id='ch_foo')
charge.description = 'test'
charge.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_foo',
{
'description': 'test',
},
None
)
def test_unset_metadata(self):
charge = libraries.stripe.Charge(id='ch_foo')
charge.metadata = {}
charge.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_foo',
{
'metadata': {},
},
None
)
def test_whole_update(self):
charge = libraries.stripe.Charge(id='ch_foo')
charge.metadata = {'whole': 'update'}
charge.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_foo',
{
'metadata': {'whole': 'update'},
},
None
)
def test_individual_delete(self):
charge = libraries.stripe.Charge(id='ch_foo')
charge.metadata = {'whole': None}
charge.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_foo',
{
'metadata': {'whole': None},
},
None
)
+42
View File
@@ -0,0 +1,42 @@
import libraries.stripe
from libraries.stripe.test.helper import (
StripeResourceTest, DUMMY_PLAN
)
class PlanTest(StripeResourceTest):
def test_create_plan(self):
libraries.stripe.Plan.create(**DUMMY_PLAN)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/plans',
DUMMY_PLAN,
None
)
def test_delete_plan(self):
p = libraries.stripe.Plan(id="pl_delete")
p.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/plans/pl_delete',
{},
None
)
def test_update_plan(self):
p = libraries.stripe.Plan(id="pl_update")
p.name = "Plan Name"
p.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/plans/pl_update',
{
'name': 'Plan Name',
},
None
)
+75
View File
@@ -0,0 +1,75 @@
import libraries.stripe
from libraries.stripe.test.helper import (
StripeResourceTest, DUMMY_CARD
)
class RecipientTest(StripeResourceTest):
def test_list_recipients(self):
libraries.stripe.Recipient.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/recipients',
{}
)
def test_recipient_transfers(self):
recipient = libraries.stripe.Recipient(id='rp_transfer')
recipient.transfers()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/transfers',
{'recipient': 'rp_transfer'},
)
def test_recipient_add_card(self):
recipient = libraries.stripe.Recipient.construct_from({
'id': 'rp_add_card',
'sources': {
'object': 'list',
'url': '/v1/recipients/rp_add_card/sources',
},
}, 'api_key')
recipient.sources.create(card=DUMMY_CARD)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/recipients/rp_add_card/sources',
{
'card': DUMMY_CARD,
},
None
)
def test_recipient_update_card(self):
card = libraries.stripe.Card.construct_from({
'recipient': 'rp_update_card',
'id': 'ca_update_card',
}, 'api_key')
card.name = 'The Best'
card.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/recipients/rp_update_card/cards/ca_update_card',
{
'name': 'The Best',
},
None
)
def test_recipient_delete_card(self):
card = libraries.stripe.Card.construct_from({
'recipient': 'rp_delete_card',
'id': 'ca_delete_card',
}, 'api_key')
card.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/recipients/rp_delete_card/cards/ca_delete_card',
{},
None
)
+161
View File
@@ -0,0 +1,161 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class RefundTest(StripeResourceTest):
def test_create_refund(self):
libraries.stripe.Refund.create(charge='ch_foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/refunds',
{'charge': 'ch_foo'},
None
)
def test_fetch_refund(self):
libraries.stripe.Refund.retrieve('re_foo')
self.requestor_mock.request.assert_called_with(
'get',
'/v1/refunds/re_foo',
{},
None
)
def test_list_refunds(self):
libraries.stripe.Refund.list(limit=3, charge='ch_foo')
self.requestor_mock.request.assert_called_with(
'get',
'/v1/refunds',
{'limit': 3, 'charge': 'ch_foo'}
)
def test_update_refund(self):
refund = libraries.stripe.resource.Refund.construct_from({
'id': "ref_update",
'charge': "ch_update",
'metadata': {},
}, 'api_key')
refund.metadata["key"] = "value"
refund.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/refunds/ref_update',
{
'metadata': {
'key': 'value',
}
},
None
)
class ChargeRefundTest(StripeResourceTest):
def test_create_refund(self):
charge = libraries.stripe.Charge.construct_from({
'id': 'ch_foo',
'refunds': {
'object': 'list',
'url': '/v1/charges/ch_foo/refunds',
}
}, 'api_key')
charge.refunds.create()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_foo/refunds',
{},
None
)
def test_non_recursive_save(self):
charge = libraries.stripe.Charge.construct_from({
'id': 'ch_nested_update',
'customer': {
'object': 'customer',
'description': 'foo',
},
'refunds': {
'object': 'list',
'url': '/v1/charges/ch_foo/refunds',
'data': [{
'id': 'ref_123',
}],
},
}, 'api_key')
charge.customer.description = 'bar'
charge.refunds.has_more = True
charge.refunds.data[0].description = 'bar'
charge.save()
# Note: ideally, we'd want the library to NOT issue requests in this
# case (i.e. the assert should actually be `assert_not_called()`).
self.requestor_mock.request.assert_called_with(
'post',
'/v1/charges/ch_nested_update',
{'refunds': {'has_more': True}},
None
)
def test_fetch_refund(self):
charge = libraries.stripe.Charge.construct_from({
'id': 'ch_get_refund',
'refunds': {
'object': 'list',
'url': '/v1/charges/ch_get_refund/refunds',
}
}, 'api_key')
charge.refunds.retrieve("ref_get")
self.requestor_mock.request.assert_called_with(
'get',
'/v1/charges/ch_get_refund/refunds/ref_get',
{},
None
)
def test_list_refunds(self):
charge = libraries.stripe.Charge.construct_from({
'id': 'ch_get_refund',
'refunds': {
'object': 'list',
'url': '/v1/charges/ch_get_refund/refunds',
}
}, 'api_key')
charge.refunds.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/charges/ch_get_refund/refunds',
{},
None
)
def test_update_refund(self):
refund = libraries.stripe.resource.Refund.construct_from({
'id': "ref_update",
'charge': "ch_update",
'metadata': {},
}, 'api_key')
refund.metadata["key"] = "value"
refund.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/refunds/ref_update',
{
'metadata': {
'key': 'value',
}
},
None
)
+90
View File
@@ -0,0 +1,90 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class ProductTest(StripeResourceTest):
def test_list_products(self):
libraries.stripe.Product.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/products',
{}
)
def test_delete_products(self):
p = libraries.stripe.Product(id='product_to_delete')
p.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/products/product_to_delete',
{},
None
)
class SKUTest(StripeResourceTest):
def test_list_skus(self):
libraries.stripe.SKU.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/skus',
{}
)
def test_delete_skus(self):
sku = libraries.stripe.SKU(id='sku_delete')
sku.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/skus/sku_delete',
{},
None
)
class OrderTest(StripeResourceTest):
def test_list_orders(self):
libraries.stripe.Order.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/orders',
{}
)
def test_pay_order(self):
order = libraries.stripe.Order(id="or_pay")
order.pay()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/orders/or_pay/pay',
{},
None
)
def test_return_order(self):
order = libraries.stripe.Order(id="or_return")
order.return_order()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/orders/or_return/returns',
{},
None
)
class OrderReturnTest(StripeResourceTest):
def test_list_order_returns(self):
libraries.stripe.OrderReturn.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/order_returns',
{}
)
+61
View File
@@ -0,0 +1,61 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class ReversalTest(StripeResourceTest):
def test_fetch_reversal(self):
transfer = libraries.stripe.Charge.construct_from({
'id': 'tr_get',
'reversals': {
'object': 'list',
'url': '/v1/transfers/tr_get/reversals',
}
}, 'api_key')
transfer.reversals.retrieve("foo")
self.requestor_mock.request.assert_called_with(
'get',
'/v1/transfers/tr_get/reversals/foo',
{},
None
)
def test_list_reversals(self):
transfer = libraries.stripe.Charge.construct_from({
'id': 'tr_list',
'reversals': {
'object': 'list',
'url': '/v1/transfers/tr_list/reversals',
}
}, 'api_key')
transfer.reversals.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/transfers/tr_list/reversals',
{},
None
)
def test_update_transfer(self):
reversal = libraries.stripe.resource.Reversal.construct_from({
'id': "rev_update",
'transfer': "tr_update",
'metadata': {},
}, 'api_key')
reversal.metadata["key"] = "value"
reversal.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/transfers/tr_update/reversals/rev_update',
{
'metadata': {
'key': 'value',
}
},
None
)
+164
View File
@@ -0,0 +1,164 @@
import pickle
import sys
import libraries.stripe
from libraries.stripe import util
from libraries.stripe.test.helper import StripeUnitTestCase, SAMPLE_INVOICE
class StripeObjectTests(StripeUnitTestCase):
def test_initializes_with_parameters(self):
obj = libraries.stripe.resource.StripeObject(
'foo', 'bar', myparam=5, yourparam='boo')
self.assertEqual('foo', obj.id)
self.assertEqual('bar', obj.api_key)
def test_access(self):
obj = libraries.stripe.resource.StripeObject('myid', 'mykey', myparam=5)
# Empty
self.assertRaises(AttributeError, getattr, obj, 'myattr')
self.assertRaises(KeyError, obj.__getitem__, 'myattr')
self.assertEqual('def', obj.get('myattr', 'def'))
self.assertEqual(None, obj.get('myattr'))
# Setters
obj.myattr = 'myval'
obj['myitem'] = 'itval'
self.assertEqual('sdef', obj.setdefault('mydef', 'sdef'))
# Getters
self.assertEqual('myval', obj.setdefault('myattr', 'sdef'))
self.assertEqual('myval', obj.myattr)
self.assertEqual('myval', obj['myattr'])
self.assertEqual('myval', obj.get('myattr'))
self.assertEqual(['id', 'myattr', 'mydef', 'myitem'],
sorted(obj.keys()))
self.assertEqual(['itval', 'myid', 'myval', 'sdef'],
sorted(obj.values()))
# Illegal operations
self.assertRaises(ValueError, setattr, obj, 'foo', '')
def test_refresh_from(self):
obj = libraries.stripe.resource.StripeObject.construct_from({
'foo': 'bar',
'trans': 'me',
}, 'mykey')
self.assertEqual('mykey', obj.api_key)
self.assertEqual('bar', obj.foo)
self.assertEqual('me', obj['trans'])
self.assertEqual(None, obj.stripe_account)
obj.refresh_from({
'foo': 'baz',
'johnny': 5,
}, 'key2', stripe_account='acct_foo')
self.assertEqual(5, obj.johnny)
self.assertEqual('baz', obj.foo)
self.assertRaises(AttributeError, getattr, obj, 'trans')
self.assertEqual('key2', obj.api_key)
self.assertEqual('acct_foo', obj.stripe_account)
obj.refresh_from({
'trans': 4,
'metadata': {'amount': 42}
}, 'key2', True)
self.assertEqual('baz', obj.foo)
self.assertEqual(4, obj.trans)
def test_passing_nested_refresh(self):
obj = libraries.stripe.resource.StripeObject.construct_from({
'foos': {
'type': 'list',
'data': [
{'id': 'nested'}
],
}
}, 'key', stripe_account='acct_foo')
nested = obj.foos.data[0]
self.assertEqual('key', obj.api_key)
self.assertEqual('nested', nested.id)
self.assertEqual('key', nested.api_key)
self.assertEqual('acct_foo', nested.stripe_account)
def test_refresh_from_nested_object(self):
obj = libraries.stripe.resource.StripeObject.construct_from(
SAMPLE_INVOICE, 'key')
self.assertEqual(1, len(obj.lines.subscriptions))
self.assertTrue(
isinstance(obj.lines.subscriptions[0],
libraries.stripe.resource.StripeObject))
self.assertEqual('month', obj.lines.subscriptions[0].plan.interval)
def test_to_json(self):
obj = libraries.stripe.resource.StripeObject.construct_from(
SAMPLE_INVOICE, 'key')
self.check_invoice_data(util.json.loads(str(obj)))
def check_invoice_data(self, data):
# Check rough structure
self.assertEqual(20, len(list(data.keys())))
self.assertEqual(3, len(list(data['lines'].keys())))
self.assertEqual(0, len(data['lines']['invoiceitems']))
self.assertEqual(1, len(data['lines']['subscriptions']))
# Check various data types
self.assertEqual(1338238728, data['date'])
self.assertEqual(None, data['next_payment_attempt'])
self.assertEqual(False, data['livemode'])
self.assertEqual('month',
data['lines']['subscriptions'][0]['plan']['interval'])
def test_repr(self):
obj = libraries.stripe.resource.StripeObject(
'foo', 'bar', myparam=5)
obj['object'] = '\u4e00boo\u1f00'
res = repr(obj)
if sys.version_info[0] < 3:
res = str(repr(obj), 'utf-8')
self.assertTrue('<StripeObject \u4e00boo\u1f00' in res)
self.assertTrue('id=foo' in res)
def test_pickling(self):
obj = libraries.stripe.resource.StripeObject(
'foo', 'bar', myparam=5)
obj['object'] = 'boo'
obj.refresh_from({'fala': 'lalala'}, api_key='bar', partial=True)
self.assertEqual('lalala', obj.fala)
pickled = pickle.dumps(obj)
newobj = pickle.loads(pickled)
self.assertEqual('foo', newobj.id)
self.assertEqual('bar', newobj.api_key)
self.assertEqual('boo', newobj['object'])
self.assertEqual('lalala', newobj.fala)
def test_deletion(self):
obj = libraries.stripe.resource.StripeObject('id', 'key')
obj.coupon = "foo"
self.assertEqual('foo', obj.coupon)
del obj.coupon
self.assertRaises(AttributeError, getattr, obj, 'coupon')
obj.refresh_from({'coupon': 'foo'}, api_key='bar', partial=True)
self.assertEqual('foo', obj.coupon)
+116
View File
@@ -0,0 +1,116 @@
import datetime
import time
import libraries.stripe
from libraries.stripe.test.helper import (
StripeResourceTest, DUMMY_PLAN
)
class SubscriptionTest(StripeResourceTest):
def test_list_subscriptions(self):
libraries.stripe.Subscription.all(customer="test_cus", plan=DUMMY_PLAN['id'],
limit=3)
self.requestor_mock.request.assert_called_with(
'get',
'/v1/subscriptions',
{
'customer': 'test_cus',
'plan': DUMMY_PLAN['id'],
'limit': 3,
},
)
def test_retrieve_subscription(self):
libraries.stripe.Subscription.retrieve('test_sub')
self.requestor_mock.request.assert_called_with(
'get',
'/v1/subscriptions/test_sub',
{},
None
)
def test_create_subscription(self):
libraries.stripe.Subscription.create(customer="test_cus", plan=DUMMY_PLAN['id'])
self.requestor_mock.request.assert_called_with(
'post',
'/v1/subscriptions',
{
'customer': 'test_cus',
'plan': DUMMY_PLAN['id']
},
None
)
def test_update_subscription(self):
subscription = libraries.stripe.Subscription.construct_from({
'id': 'test_sub',
'customer': 'test_cus',
}, 'api_key')
trial_end_dttm = datetime.datetime.now() + datetime.timedelta(days=15)
trial_end_int = int(time.mktime(trial_end_dttm.timetuple()))
subscription.plan = DUMMY_PLAN['id']
subscription.trial_end = trial_end_int
subscription.save()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/subscriptions/test_sub',
{
'plan': DUMMY_PLAN['id'],
'trial_end': trial_end_int
},
None
)
def test_modify_subscription(self):
trial_end_dttm = datetime.datetime.now() + datetime.timedelta(days=15)
trial_end_int = int(time.mktime(trial_end_dttm.timetuple()))
libraries.stripe.Subscription.modify('test_sub',
plan=DUMMY_PLAN['id'],
trial_end=trial_end_int)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/subscriptions/test_sub',
{
'plan': DUMMY_PLAN['id'],
'trial_end': trial_end_int
},
None
)
def test_delete_subscription(self):
subscription = libraries.stripe.Subscription.construct_from({
'id': 'test_sub',
'customer': 'test_cus',
}, 'api_key')
subscription.delete()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/subscriptions/test_sub',
{},
None
)
def test_delete_subscription_discount(self):
subscription = libraries.stripe.Subscription.construct_from({
'id': 'test_sub',
'customer': 'test_cus',
'coupon': 'test_discount'
}, 'api_key')
subscription.delete_discount()
self.requestor_mock.request.assert_called_with(
'delete',
'/v1/subscriptions/test_sub/discount'
)
+25
View File
@@ -0,0 +1,25 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class ThreeDSecureTest(StripeResourceTest):
def test_threedsecure_create(self):
libraries.stripe.ThreeDSecure.create(
card="tok_test",
amount=1500,
currency="usd",
return_url="https://example.org/3d-secure-result"
)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/3d_secure',
{
'card': 'tok_test',
'amount': 1500,
'currency': 'usd',
'return_url': 'https://example.org/3d-secure-result'
},
None
)
+24
View File
@@ -0,0 +1,24 @@
import libraries.stripe
from libraries.stripe.test.helper import StripeResourceTest
class TransferTest(StripeResourceTest):
def test_list_transfers(self):
libraries.stripe.Transfer.list()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/transfers',
{}
)
def test_cancel_transfer(self):
transfer = libraries.stripe.Transfer(id='tr_cancel')
transfer.cancel()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/transfers/tr_cancel/cancel',
{},
None
)
+366
View File
@@ -0,0 +1,366 @@
from libraries.stripe.test.helper import (
StripeApiTestCase, MyUpdateable
)
class UpdateableAPIResourceTests(StripeApiTestCase):
def setUp(self):
super(UpdateableAPIResourceTests, self).setUp()
self.mock_response({
'thats': 'it'
})
self.obj = MyUpdateable.construct_from({
'id': 'myid',
'foo': 'bar',
'baz': 'boz',
'metadata': {
'size': 'l',
'score': 4,
'height': 10
}
}, 'mykey')
def checkSave(self):
self.assertTrue(self.obj is self.obj.save())
self.assertEqual('it', self.obj.thats)
# TODO: Should we force id to be retained?
# self.assertEqual('myid', obj.id)
self.assertRaises(AttributeError, getattr, self.obj, 'baz')
def test_idempotent_save(self):
self.obj.baz = 'updated'
self.obj.save(idempotency_key='foo')
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'metadata': {},
'baz': 'updated',
},
{
'Idempotency-Key': 'foo',
},
)
def test_save(self):
self.obj.baz = 'updated'
self.obj.other = 'newval'
self.obj.metadata.size = 'm'
self.obj.metadata.info = 'a2'
self.obj.metadata.height = None
self.checkSave()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'baz': 'updated',
'other': 'newval',
'metadata': {
'size': 'm',
'info': 'a2',
'height': '',
}
},
None
)
def test_add_key_to_nested_object(self):
acct = MyUpdateable.construct_from({
'id': 'myid',
'legal_entity': {
'size': 'l',
'score': 4,
'height': 10
}
}, 'mykey')
acct.legal_entity['first_name'] = 'bob'
self.assertTrue(acct is acct.save())
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'legal_entity': {
'first_name': 'bob',
}
},
None
)
def test_save_nothing(self):
acct = MyUpdateable.construct_from({
'id': 'myid',
'metadata': {
'key': 'value',
}
}, 'mykey')
self.assertTrue(acct is acct.save())
# Note: ideally, we'd want the library to NOT issue requests in this
# case (i.e. the assert should actually be `assert_not_called()`).
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{'metadata': {}},
None
)
def test_replace_nested_object(self):
acct = MyUpdateable.construct_from({
'id': 'myid',
'legal_entity': {
'last_name': 'smith',
}
}, 'mykey')
acct.legal_entity = {
'first_name': 'bob',
}
self.assertTrue(acct is acct.save())
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'legal_entity': {
'first_name': 'bob',
'last_name': '',
}
},
None
)
def test_array_setting(self):
acct = MyUpdateable.construct_from({
'id': 'myid',
'legal_entity': {}
}, 'mykey')
acct.legal_entity.additional_owners = [{'first_name': 'Bob'}]
self.assertTrue(acct is acct.save())
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'legal_entity': {
'additional_owners': [
{'first_name': 'Bob'}
]
}
},
None
)
def test_array_none(self):
acct = MyUpdateable.construct_from({
'id': 'myid',
'legal_entity': {
'additional_owners': None,
}
}, 'mykey')
acct.foo = 'bar'
self.assertTrue(acct is acct.save())
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'foo': 'bar',
'legal_entity': {},
},
None
)
def test_array_insertion(self):
acct = MyUpdateable.construct_from({
'id': 'myid',
'legal_entity': {
'additional_owners': []
}
}, 'mykey')
acct.legal_entity.additional_owners.append({'first_name': 'Bob'})
self.assertTrue(acct is acct.save())
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'legal_entity': {
'additional_owners': {
'0': {'first_name': 'Bob'},
}
}
},
None
)
def test_array_update(self):
acct = MyUpdateable.construct_from({
'id': 'myid',
'legal_entity': {
'additional_owners': [
{'first_name': 'Bob'},
{'first_name': 'Jane'}
]
}
}, 'mykey')
acct.legal_entity.additional_owners[1].first_name = 'Janet'
self.assertTrue(acct is acct.save())
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'legal_entity': {
'additional_owners': {
'0': {},
'1': {'first_name': 'Janet'}
}
}
},
None
)
def test_array_noop(self):
acct = MyUpdateable.construct_from({
'id': 'myid',
'legal_entity': {
'additional_owners': [{'first_name': 'Bob'}]
},
'currencies_supported': ['usd', 'cad']
}, 'mykey')
self.assertTrue(acct is acct.save())
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'legal_entity': {'additional_owners': {'0': {}}}
},
None
)
def test_hash_noop(self):
acct = MyUpdateable.construct_from({
'id': 'myid',
'legal_entity': {
'address': {'line1': '1 Two Three'}
}
}, 'mykey')
self.assertTrue(acct is acct.save())
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{'legal_entity': {'address': {}}},
None
)
def test_save_replace_metadata_with_number(self):
self.obj.baz = 'updated'
self.obj.other = 'newval'
self.obj.metadata = 3
self.checkSave()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'baz': 'updated',
'other': 'newval',
'metadata': 3,
},
None
)
def test_save_overwrite_metadata(self):
self.obj.metadata = {}
self.checkSave()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'metadata': {
'size': '',
'score': '',
'height': '',
}
},
None
)
def test_save_replace_metadata(self):
self.obj.baz = 'updated'
self.obj.other = 'newval'
self.obj.metadata = {
'size': 'm',
'info': 'a2',
'score': 4,
}
self.checkSave()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'baz': 'updated',
'other': 'newval',
'metadata': {
'size': 'm',
'info': 'a2',
'height': '',
'score': 4,
}
},
None
)
def test_save_update_metadata(self):
self.obj.baz = 'updated'
self.obj.other = 'newval'
self.obj.metadata.update({
'size': 'm',
'info': 'a2',
'score': 4,
})
self.checkSave()
self.requestor_mock.request.assert_called_with(
'post',
'/v1/myupdateables/myid',
{
'baz': 'updated',
'other': 'newval',
'metadata': {
'size': 'm',
'info': 'a2',
'score': 4,
}
},
None
)
+37
View File
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
import sys
import unittest2
from libraries.stripe import StripeError
from libraries.stripe.test.helper import StripeUnitTestCase
class StripeErrorTests(StripeUnitTestCase):
def test_formatting(self):
err = StripeError('öre')
if sys.version_info > (3, 0):
assert str(err) == 'öre'
else:
assert str(err) == '\xc3\xb6re'
assert str(err) == 'öre'
def test_formatting_with_request_id(self):
err = StripeError('öre', headers={'request-id': '123'})
if sys.version_info > (3, 0):
assert str(err) == 'Request 123: öre'
else:
assert str(err) == 'Request 123: \xc3\xb6re'
assert str(err) == 'Request 123: öre'
def test_formatting_with_none(self):
err = StripeError(None, headers={'request-id': '123'})
if sys.version_info > (3, 0):
assert str(err) == 'Request 123: <empty message>'
else:
assert str(err) == 'Request 123: <empty message>'
assert str(err) == 'Request 123: <empty message>'
if __name__ == '__main__':
unittest2.main()
+266
View File
@@ -0,0 +1,266 @@
import sys
import unittest2
if 1/2 != 0: str=str
from mock import Mock, patch
import libraries.stripe
from libraries.stripe.test.helper import StripeUnitTestCase
VALID_API_METHODS = ('get', 'post', 'delete')
class HttpClientTests(StripeUnitTestCase):
def setUp(self):
super(HttpClientTests, self).setUp()
self.original_filters = libraries.stripe.http_client.warnings.filters[:]
libraries.stripe.http_client.warnings.simplefilter('ignore')
def tearDown(self):
libraries.stripe.http_client.warnings.filters = self.original_filters
super(HttpClientTests, self).tearDown()
def check_default(self, none_libs, expected):
for lib in none_libs:
setattr(stripe.http_client, lib, None)
inst = libraries.stripe.http_client.new_default_http_client()
self.assertTrue(isinstance(inst, expected))
def test_new_default_http_client_urlfetch(self):
self.check_default((),
libraries.stripe.http_client.UrlFetchClient)
def test_new_default_http_client_requests(self):
self.check_default(('urlfetch',),
libraries.stripe.http_client.RequestsClient)
def test_new_default_http_client_pycurl(self):
self.check_default(('urlfetch', 'requests',),
libraries.stripe.http_client.PycurlClient)
def test_new_default_http_client_urllib2(self):
self.check_default(('urlfetch', 'requests', 'pycurl'),
libraries.stripe.http_client.Urllib2Client)
class ClientTestBase():
@property
def request_mock(self):
return self.request_mocks[self.request_client.name]
@property
def valid_url(self, path='/foo'):
return 'https://api.stripe.com%s' % (path,)
def make_request(self, method, url, headers, post_data):
client = self.request_client(verify_ssl_certs=True)
return client.request(method, url, headers, post_data)
def mock_response(self, body, code):
raise NotImplementedError(
'You must implement this in your test subclass')
def mock_error(self, error):
raise NotImplementedError(
'You must implement this in your test subclass')
def check_call(self, meth, abs_url, headers, params):
raise NotImplementedError(
'You must implement this in your test subclass')
def test_request(self):
self.mock_response(self.request_mock, '{"foo": "baz"}', 200)
for meth in VALID_API_METHODS:
abs_url = self.valid_url
data = ''
if meth != 'post':
abs_url = '%s?%s' % (abs_url, data)
data = None
headers = {'my-header': 'header val'}
body, code, _ = self.make_request(
meth, abs_url, headers, data)
self.assertEqual(200, code)
self.assertEqual('{"foo": "baz"}', body)
self.check_call(self.request_mock, meth, abs_url,
data, headers)
def test_exception(self):
self.mock_error(self.request_mock)
self.assertRaises(stripe.error.APIConnectionError,
self.make_request,
'get', self.valid_url, {}, None)
class RequestsVerify(object):
def __eq__(self, other):
return other and other.endswith('stripe/data/ca-certificates.crt')
class RequestsClientTests(StripeUnitTestCase, ClientTestBase):
request_client = libraries.stripe.http_client.RequestsClient
def mock_response(self, mock, body, code):
result = Mock()
result.content = body
result.status_code = code
mock.request = Mock(return_value=result)
def mock_error(self, mock):
mock.exceptions.RequestException = Exception
mock.request.side_effect = mock.exceptions.RequestException()
def check_call(self, mock, meth, url, post_data, headers):
mock.request.assert_called_with(meth, url,
headers=headers,
data=post_data,
verify=RequestsVerify(),
timeout=80)
class UrlFetchClientTests(StripeUnitTestCase, ClientTestBase):
request_client = libraries.stripe.http_client.UrlFetchClient
def mock_response(self, mock, body, code):
result = Mock()
result.content = body
result.status_code = code
mock.fetch = Mock(return_value=result)
def mock_error(self, mock):
mock.Error = mock.InvalidURLError = Exception
mock.fetch.side_effect = mock.InvalidURLError()
def check_call(self, mock, meth, url, post_data, headers):
mock.fetch.assert_called_with(
url=url,
method=meth,
headers=headers,
validate_certificate=True,
deadline=55,
payload=post_data
)
class Urllib2ClientTests(StripeUnitTestCase, ClientTestBase):
request_client = libraries.stripe.http_client.Urllib2Client
def mock_response(self, mock, body, code):
response = Mock
response.read = Mock(return_value=body)
response.code = code
response.info = Mock(return_value={})
self.request_object = Mock()
mock.Request = Mock(return_value=self.request_object)
mock.urlopen = Mock(return_value=response)
def mock_error(self, mock):
mock.urlopen.side_effect = ValueError
def check_call(self, mock, meth, url, post_data, headers):
if sys.version_info >= (3, 0) and isinstance(post_data, str):
post_data = post_data.encode('utf-8')
mock.Request.assert_called_with(url, post_data, headers)
mock.urlopen.assert_called_with(self.request_object)
class PycurlClientTests(StripeUnitTestCase, ClientTestBase):
request_client = libraries.stripe.http_client.PycurlClient
@property
def request_mock(self):
if not hasattr(self, 'curl_mock'):
lib_mock = self.request_mocks[self.request_client.name]
self.curl_mock = Mock()
lib_mock.Curl = Mock(return_value=self.curl_mock)
return self.curl_mock
def setUp(self):
super(PycurlClientTests, self).setUp()
self.sio_patcher = patch('stripe.util.StringIO.StringIO')
sio_mock = Mock()
self.sio_patcher.start().return_value = sio_mock
self.sio_getvalue = sio_mock.getvalue
def tearDown(self):
super(PycurlClientTests, self).tearDown()
self.sio_patcher.stop()
def mock_response(self, mock, body, code):
self.sio_getvalue.return_value = body
mock.getinfo.return_value = code
def mock_error(self, mock):
class FakeException(BaseException):
def __getitem__(self, i):
return 'foo'
libraries.stripe.http_client.pycurl.error = FakeException
mock.perform.side_effect = libraries.stripe.http_client.pycurl.error
def check_call(self, mock, meth, url, post_data, headers):
# TODO: Check the setopt calls
pass
class APIEncodeTest(StripeUnitTestCase):
def test_encode_dict(self):
body = {
'foo': {
'dob': {
'month': 1,
},
'name': 'bat'
},
}
values = [t for t in libraries.stripe.api_requestor._api_encode(body)]
self.assertTrue(('foo[dob][month]', 1) in values)
self.assertTrue(('foo[name]', 'bat') in values)
def test_encode_array(self):
body = {
'foo': [{
'dob': {
'month': 1,
},
'name': 'bat'
}],
}
values = [t for t in libraries.stripe.api_requestor._api_encode(body)]
self.assertTrue(('foo[][dob][month]', 1) in values)
self.assertTrue(('foo[][name]', 'bat') in values)
if __name__ == '__main__':
unittest2.main()
+178
View File
@@ -0,0 +1,178 @@
# -*- coding: utf-8 -*-
import os
import sys
import unittest2
import libraries.stripe
if 1/2 != 0: str=str
from mock import patch
from libraries.stripe.test.helper import (StripeTestCase, NOW, DUMMY_CHARGE, DUMMY_CARD)
class FunctionalTests(StripeTestCase):
request_client = libraries.stripe.http_client.Urllib2Client
def setUp(self):
super(FunctionalTests, self).setUp()
def get_http_client(*args, **kwargs):
return self.request_client(*args, **kwargs)
self.client_patcher = patch(
'stripe.http_client.new_default_http_client')
client_mock = self.client_patcher.start()
client_mock.side_effect = get_http_client
def tearDown(self):
super(FunctionalTests, self).tearDown()
self.client_patcher.stop()
def test_dns_failure(self):
api_base = libraries.stripe.api_base
try:
libraries.stripe.api_base = 'https://my-invalid-domain.ireallywontresolve/v1'
self.assertRaises(stripe.error.APIConnectionError,
libraries.stripe.Customer.create)
finally:
libraries.stripe.api_base = api_base
def test_run(self):
charge = libraries.stripe.Charge.create(**DUMMY_CHARGE)
self.assertFalse(charge.refunded)
charge.refund()
self.assertTrue(charge.refunded)
def test_refresh(self):
charge = libraries.stripe.Charge.create(**DUMMY_CHARGE)
charge2 = libraries.stripe.Charge.retrieve(charge.id)
self.assertEqual(charge2.created, charge.created)
charge2.junk = 'junk'
charge2.refresh()
self.assertRaises(AttributeError, lambda: charge2.junk)
def test_list_accessors(self):
customer = libraries.stripe.Customer.create(card=DUMMY_CARD)
self.assertEqual(customer['created'], customer.created)
customer['foo'] = 'bar'
self.assertEqual(customer.foo, 'bar')
def test_raise(self):
EXPIRED_CARD = DUMMY_CARD.copy()
EXPIRED_CARD['exp_month'] = NOW.month - 2
EXPIRED_CARD['exp_year'] = NOW.year - 2
self.assertRaises(stripe.error.CardError, libraries.stripe.Charge.create,
amount=100, currency='usd', card=EXPIRED_CARD)
def test_response_headers(self):
EXPIRED_CARD = DUMMY_CARD.copy()
EXPIRED_CARD['exp_month'] = NOW.month - 2
EXPIRED_CARD['exp_year'] = NOW.year - 2
try:
libraries.stripe.Charge.create(amount=100, currency='usd', card=EXPIRED_CARD)
self.fail('charge creation with expired card did not fail')
except libraries.stripe.error.CardError as e:
self.assertTrue(e.request_id.startswith('req_'))
def test_unicode(self):
# Make sure unicode requests can be sent
self.assertRaises(stripe.error.InvalidRequestError,
libraries.stripe.Charge.retrieve,
id='☃')
def test_none_values(self):
customer = libraries.stripe.Customer.create(plan=None)
self.assertTrue(customer.id)
def test_missing_id(self):
customer = libraries.stripe.Customer()
self.assertRaises(stripe.error.InvalidRequestError, customer.refresh)
class RequestsFunctionalTests(FunctionalTests):
request_client = libraries.stripe.http_client.RequestsClient
class UrlfetchFunctionalTests(FunctionalTests):
request_client = 'urlfetch'
def setUp(self):
if libraries.stripe.http_client.urlfetch is None:
self.skipTest(
'`urlfetch` from Google App Engine is unavailable.')
else:
super(UrlfetchFunctionalTests, self).setUp()
class PycurlFunctionalTests(FunctionalTests):
def setUp(self):
if not os.environ.get('STRIPE_TEST_PYCURL'):
self.skipTest('Pycurl skipped as STRIPE_TEST_PYCURL is not set')
if sys.version_info >= (3, 0):
self.skipTest('Pycurl is not supported in Python 3')
else:
super(PycurlFunctionalTests, self).setUp()
request_client = libraries.stripe.http_client.PycurlClient
class AuthenticationErrorTest(StripeTestCase):
def test_invalid_credentials(self):
key = libraries.stripe.api_key
try:
libraries.stripe.api_key = 'invalid'
libraries.stripe.Customer.create()
except libraries.stripe.error.AuthenticationError as e:
self.assertEqual(401, e.http_status)
self.assertTrue(isinstance(e.http_body, str))
self.assertTrue(isinstance(e.json_body, dict))
# Note that an invalid API key bypasses many of the standard
# facilities in the API server so currently no Request ID is
# returned.
finally:
libraries.stripe.api_key = key
class CardErrorTest(StripeTestCase):
def test_declined_card_props(self):
EXPIRED_CARD = DUMMY_CARD.copy()
EXPIRED_CARD['exp_month'] = NOW.month - 2
EXPIRED_CARD['exp_year'] = NOW.year - 2
try:
libraries.stripe.Charge.create(amount=100, currency='usd', card=EXPIRED_CARD)
except libraries.stripe.error.CardError as e:
self.assertEqual(402, e.http_status)
self.assertTrue(isinstance(e.http_body, str))
self.assertTrue(isinstance(e.json_body, dict))
self.assertTrue(e.request_id.startswith('req_'))
class InvalidRequestErrorTest(StripeTestCase):
def test_nonexistent_object(self):
try:
libraries.stripe.Charge.retrieve('invalid')
except libraries.stripe.error.InvalidRequestError as e:
self.assertEqual(404, e.http_status)
self.assertTrue(isinstance(e.http_body, str))
self.assertTrue(isinstance(e.json_body, dict))
self.assertTrue(e.request_id.startswith('req_'))
def test_invalid_data(self):
try:
libraries.stripe.Charge.create()
except libraries.stripe.error.InvalidRequestError as e:
self.assertEqual(400, e.http_status)
self.assertTrue(isinstance(e.http_body, str))
self.assertTrue(isinstance(e.json_body, dict))
self.assertTrue(e.request_id.startswith('req_'))
if __name__ == '__main__':
unittest2.main()
+52
View File
@@ -0,0 +1,52 @@
# -*- encoding: utf-8 -*-
import re
import sys
from libraries.stripe.multipart_data_generator import MultipartDataGenerator
from libraries.stripe.test.helper import StripeTestCase
class MultipartDataGeneratorTests(StripeTestCase):
def run_test_multipart_data_with_file(self, test_file):
params = {
"key1": b"ASCII value",
"key2": "Üñìçôdé value",
"key3": test_file
}
generator = MultipartDataGenerator()
generator.add_params(params)
http_body = generator.get_post_data()
if sys.version_info >= (3,):
http_body = http_body.decode('utf-8')
self.assertTrue(re.search(
r"Content-Disposition: form-data; name=\"key1\"", http_body))
self.assertTrue(re.search(r"ASCII value", http_body))
self.assertTrue(re.search(
r"Content-Disposition: form-data; name=\"key2\"", http_body))
self.assertTrue(re.search(r"Üñìçôdé value", http_body))
self.assertTrue(re.search(
r"Content-Disposition: form-data; name=\"key3\"; "
r"filename=\".+\"",
http_body))
self.assertTrue(re.search(
r"Content-Type: application/octet-stream", http_body))
test_file.seek(0)
file_contents = test_file.read()
if sys.version_info >= (3,) and isinstance(file_contents, bytes):
file_contents = file_contents.decode('utf-8')
self.assertNotEqual(-1, http_body.find(file_contents))
def test_multipart_data_file_text(self):
with open(__file__, mode='r') as test_file:
self.run_test_multipart_data_with_file(test_file)
def test_multipart_data_file_binary(self):
with open(__file__, mode='rb') as test_file:
self.run_test_multipart_data_with_file(test_file)
+417
View File
@@ -0,0 +1,417 @@
import datetime
import unittest2
import urllib.parse
from mock import Mock, ANY
import libraries.stripe
from libraries.stripe.test.helper import StripeUnitTestCase
VALID_API_METHODS = ('get', 'post', 'delete')
class GMT1(datetime.tzinfo):
def utcoffset(self, dt):
return datetime.timedelta(hours=1)
def dst(self, dt):
return datetime.timedelta(0)
def tzname(self, dt):
return "Europe/Prague"
class APIHeaderMatcher(object):
EXP_KEYS = ['X-Stripe-Client-User-Agent', 'User-Agent', 'Authorization']
METHOD_EXTRA_KEYS = {"post": ["Content-Type"]}
def __init__(self, api_key=None, extra={}, request_method=None):
self.request_method = request_method
self.api_key = api_key or libraries.stripe.api_key
self.extra = extra
def __eq__(self, other):
return (self._keys_match(other) and
self._auth_match(other) and
self._extra_match(other))
def _keys_match(self, other):
expected_keys = self.EXP_KEYS + list(self.extra.keys())
if self.request_method is not None and self.request_method in \
self.METHOD_EXTRA_KEYS:
expected_keys.extend(self.METHOD_EXTRA_KEYS[self.request_method])
return (sorted(other.keys()) == sorted(expected_keys))
def _auth_match(self, other):
return other['Authorization'] == "Bearer %s" % (self.api_key,)
def _extra_match(self, other):
for k, v in self.extra.items():
if other[k] != v:
return False
return True
class QueryMatcher(object):
def __init__(self, expected):
self.expected = sorted(expected)
def __eq__(self, other):
query = urllib.parse.urlsplit(other).query or other
parsed = libraries.stripe.util.parse_qsl(query)
return self.expected == sorted(parsed)
class UrlMatcher(object):
def __init__(self, expected):
self.exp_parts = urllib.parse.urlsplit(expected)
def __eq__(self, other):
other_parts = urllib.parse.urlsplit(other)
for part in ('scheme', 'netloc', 'path', 'fragment'):
expected = getattr(self.exp_parts, part)
actual = getattr(other_parts, part)
if expected != actual:
print('Expected %s "%s" but got "%s"' % (
part, expected, actual))
return False
q_matcher = QueryMatcher(stripe.util.parse_qsl(self.exp_parts.query))
return q_matcher == other
class APIRequestorRequestTests(StripeUnitTestCase):
ENCODE_INPUTS = {
'dict': {
'astring': 'bar',
'anint': 5,
'anull': None,
'adatetime': datetime.datetime(2013, 1, 1, tzinfo=GMT1()),
'atuple': (1, 2),
'adict': {'foo': 'bar', 'boz': 5},
'alist': ['foo', 'bar'],
},
'list': [1, 'foo', 'baz'],
'string': 'boo',
'unicode': '\u1234',
'datetime': datetime.datetime(2013, 1, 1, second=1, tzinfo=GMT1()),
'none': None,
}
ENCODE_EXPECTATIONS = {
'dict': [
('%s[astring]', 'bar'),
('%s[anint]', 5),
('%s[adatetime]', 1356994800),
('%s[adict][foo]', 'bar'),
('%s[adict][boz]', 5),
('%s[alist][]', 'foo'),
('%s[alist][]', 'bar'),
('%s[atuple][]', 1),
('%s[atuple][]', 2),
],
'list': [
('%s[]', 1),
('%s[]', 'foo'),
('%s[]', 'baz'),
],
'string': [('%s', 'boo')],
'unicode': [('%s', libraries.stripe.util.utf8('\u1234'))],
'datetime': [('%s', 1356994801)],
'none': [],
}
def setUp(self):
super(APIRequestorRequestTests, self).setUp()
self.http_client = Mock(stripe.http_client.HTTPClient)
self.http_client._verify_ssl_certs = True
self.http_client.name = 'mockclient'
self.requestor = libraries.stripe.api_requestor.APIRequestor(
client=self.http_client)
def mock_response(self, return_body, return_code, requestor=None,
headers=None):
if not requestor:
requestor = self.requestor
self.http_client.request = Mock(
return_value=(return_body, return_code, headers or {}))
def check_call(self, meth, abs_url=None, headers=None,
post_data=None, requestor=None):
if not abs_url:
abs_url = 'https://api.stripe.com%s' % (self.valid_path,)
if not requestor:
requestor = self.requestor
if not headers:
headers = APIHeaderMatcher(request_method=meth)
self.http_client.request.assert_called_with(
meth, abs_url, headers, post_data)
@property
def valid_path(self):
return '/foo'
def encoder_check(self, key):
stk_key = "my%s" % (key,)
value = self.ENCODE_INPUTS[key]
expectation = [(k % (stk_key,), v) for k, v in
self.ENCODE_EXPECTATIONS[key]]
stk = []
fn = getattr(stripe.api_requestor.APIRequestor, "encode_%s" % (key,))
fn(stk, stk_key, value)
if isinstance(value, dict):
expectation.sort()
stk.sort()
self.assertEqual(expectation, stk)
def _test_encode_naive_datetime(self):
stk = []
libraries.stripe.api_requestor.APIRequestor.encode_datetime(
stk, 'test', datetime.datetime(2013, 1, 1))
# Naive datetimes will encode differently depending on your system
# local time. Since we don't know the local time of your system,
# we just check that naive encodings are within 24 hours of correct.
self.assertTrue(60 * 60 * 24 > abs(stk[0][1] - 1356994800))
def test_param_encoding(self):
self.mock_response('{}', 200)
self.requestor.request('get', '', self.ENCODE_INPUTS)
expectation = []
for type_, values in self.ENCODE_EXPECTATIONS.items():
expectation.extend([(k % (type_,), str(v)) for k, v in values])
self.check_call('get', QueryMatcher(expectation))
def test_dictionary_list_encoding(self):
params = {
'foo': {
'0': {
'bar': 'bat',
}
}
}
encoded = list(stripe.api_requestor._api_encode(params))
key, value = encoded[0]
self.assertEqual('foo[0][bar]', key)
self.assertEqual('bat', value)
def test_url_construction(self):
CASES = (
('https://api.stripe.com?foo=bar', '', {'foo': 'bar'}),
('https://api.stripe.com?foo=bar', '?', {'foo': 'bar'}),
('https://api.stripe.com', '', {}),
(
'https://api.stripe.com/%20spaced?foo=bar%24&baz=5',
'/%20spaced?foo=bar%24',
{'baz': '5'}
),
(
'https://api.stripe.com?foo=bar&foo=bar',
'?foo=bar',
{'foo': 'bar'}
),
)
for expected, url, params in CASES:
self.mock_response('{}', 200)
self.requestor.request('get', url, params)
self.check_call('get', expected)
def test_empty_methods(self):
for meth in VALID_API_METHODS:
self.mock_response('{}', 200)
body, key = self.requestor.request(meth, self.valid_path, {})
if meth == 'post':
post_data = ''
else:
post_data = None
self.check_call(meth, post_data=post_data)
self.assertEqual({}, body)
def test_methods_with_params_and_response(self):
for meth in VALID_API_METHODS:
self.mock_response('{"foo": "bar", "baz": 6}', 200)
params = {
'alist': [1, 2, 3],
'adict': {'frobble': 'bits'},
'adatetime': datetime.datetime(2013, 1, 1, tzinfo=GMT1())
}
encoded = ('adict%5Bfrobble%5D=bits&adatetime=1356994800&'
'alist%5B%5D=1&alist%5B%5D=2&alist%5B%5D=3')
body, key = self.requestor.request(meth, self.valid_path,
params)
self.assertEqual({'foo': 'bar', 'baz': 6}, body)
if meth == 'post':
self.check_call(
meth,
post_data=QueryMatcher(stripe.util.parse_qsl(encoded)))
else:
abs_url = "https://api.stripe.com%s?%s" % (
self.valid_path, encoded)
self.check_call(meth, abs_url=UrlMatcher(abs_url))
def test_uses_headers(self):
self.mock_response('{}', 200)
self.requestor.request('get', self.valid_path, {}, {'foo': 'bar'})
self.check_call('get', headers=APIHeaderMatcher(extra={'foo': 'bar'}))
def test_uses_instance_key(self):
key = 'fookey'
requestor = libraries.stripe.api_requestor.APIRequestor(key,
client=self.http_client)
self.mock_response('{}', 200, requestor=requestor)
body, used_key = requestor.request('get', self.valid_path, {})
self.check_call('get', headers=APIHeaderMatcher(
key, request_method='get'), requestor=requestor)
self.assertEqual(key, used_key)
def test_passes_api_version(self):
libraries.stripe.api_version = 'fooversion'
self.mock_response('{}', 200)
body, key = self.requestor.request('get', self.valid_path, {})
self.check_call('get', headers=APIHeaderMatcher(
extra={'Stripe-Version': 'fooversion'}, request_method='get'))
def test_uses_instance_account(self):
account = 'acct_foo'
requestor = libraries.stripe.api_requestor.APIRequestor(account=account,
client=self.http_client)
self.mock_response('{}', 200, requestor=requestor)
requestor.request('get', self.valid_path, {})
self.check_call(
'get',
requestor=requestor,
headers=APIHeaderMatcher(
extra={'Stripe-Account': account},
request_method='get'
),
)
def test_fails_without_api_key(self):
libraries.stripe.api_key = None
self.assertRaises(stripe.error.AuthenticationError,
self.requestor.request,
'get', self.valid_path, {})
def test_not_found(self):
self.mock_response('{"error": {}}', 404)
self.assertRaises(stripe.error.InvalidRequestError,
self.requestor.request,
'get', self.valid_path, {})
def test_authentication_error(self):
self.mock_response('{"error": {}}', 401)
self.assertRaises(stripe.error.AuthenticationError,
self.requestor.request,
'get', self.valid_path, {})
def test_card_error(self):
self.mock_response('{"error": {}}', 402)
self.assertRaises(stripe.error.CardError,
self.requestor.request,
'get', self.valid_path, {})
def test_rate_limit_error(self):
self.mock_response('{"error": {}}', 429)
self.assertRaises(stripe.error.RateLimitError,
self.requestor.request,
'get', self.valid_path, {})
def test_old_rate_limit_error(self):
"""
Tests legacy rate limit error pre-2015-09-18
"""
self.mock_response('{"error": {"code":"rate_limit"}}', 400)
self.assertRaises(stripe.error.RateLimitError,
self.requestor.request,
'get', self.valid_path, {})
def test_server_error(self):
self.mock_response('{"error": {}}', 500)
self.assertRaises(stripe.error.APIError,
self.requestor.request,
'get', self.valid_path, {})
def test_invalid_json(self):
self.mock_response('{', 200)
self.assertRaises(stripe.error.APIError,
self.requestor.request,
'get', self.valid_path, {})
def test_invalid_method(self):
self.assertRaises(stripe.error.APIConnectionError,
self.requestor.request,
'foo', 'bar')
class DefaultClientTests(unittest2.TestCase):
def setUp(self):
libraries.stripe.default_http_client = None
libraries.stripe.api_key = 'foo'
def test_default_http_client_called(self):
hc = Mock(stripe.http_client.HTTPClient)
hc._verify_ssl_certs = True
hc.name = 'mockclient'
hc.request = Mock(return_value=("{}", 200, {}))
libraries.stripe.default_http_client = hc
libraries.stripe.Charge.list(limit=3)
hc.request.assert_called_with(
'get', 'https://api.stripe.com/v1/charges?limit=3', ANY, None)
def tearDown(self):
libraries.stripe.api_key = None
libraries.stripe.default_http_client = None
if __name__ == '__main__':
unittest2.main()
+63
View File
@@ -0,0 +1,63 @@
import logging
import sys
import os
logger = logging.getLogger('stripe')
__all__ = ['StringIO', 'parse_qsl', 'json', 'utf8']
try:
# When cStringIO is available
import io as StringIO
except ImportError:
try:
import io
except:
from io import StringIO #for Python 3
try:
from urllib.parse import parse_qsl
except ImportError:
# Python < 2.6
try:
from cgi import parse_qsl
except:
from urllib.parse import parse_qsl #for Python 3
try:
import json
except ImportError:
json = None
if not (json and hasattr(json, 'loads')):
try:
import simplejson as json
except ImportError:
if not json:
raise ImportError(
"Stripe requires a JSON library, such as simplejson. "
"HINT: Try installing the "
"python simplejson library via 'pip install simplejson' or "
"'easy_install simplejson', or contact support@stripe.com "
"with questions.")
else:
raise ImportError(
"Stripe requires a JSON library with the same interface as "
"the Python 2.6 'json' library. You appear to have a 'json' "
"library with a different interface. Please install "
"the simplejson library. HINT: Try installing the "
"python simplejson library via 'pip install simplejson' "
"or 'easy_install simplejson', or contact support@stripe.com"
"with questions.")
def utf8(value):
if isinstance(value, str) and sys.version_info < (3, 0):
return value.encode('utf-8')
else:
return value
def is_appengine_dev():
return ('APPENGINE_RUNTIME' in os.environ and
'Dev' in os.environ.get('SERVER_SOFTWARE', ''))
+1
View File
@@ -0,0 +1 @@
VERSION = '1.37.0'
+362 -493
View File
@@ -1,504 +1,373 @@
from config import *
from functions import *
from admin import CommunityMapEditor,ArtistMapEditor,Selector,Items
from api import load_code_api
from api import *
from admin import *
from crons import *
from tests import *
from tasks import *
class MainHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self,name):
domain=gdi(self)
if 0 and "treasurehunter" in name:
whtml(self,"examples/learningPixi/treasure_hunter.html")
elif "runner" in name:
whtml(self,"htmls/runner.html",domain=domain)
elif "executor" in name:
whtml(self,"htmls/executor.html",domain=domain)
elif name=="info/cookies":
self.response.out.write(repr(self.request.cookies))
elif "logs" in name:
user=get_user(self,domain)
whtml(self,"htmls/logs.html",domain=domain,user=user)
elif "linux" in name:
user=get_user(self,domain)
whtml(self,"htmls/linux.html",domain=domain,user=user)
elif "macos" in name:
user=get_user(self,domain)
whtml(self,"htmls/macos.html",domain=domain,user=user)
elif "allnotes" in name:
user=get_user(self,domain)
whtml(self,"htmls/allnotes.html",domain=domain,user=user)
elif "roadmap" in name:
user=get_user(self,domain)
whtml(self,"htmls/roadmap.html",domain=domain,user=user)
elif "drm-free" in name:
user=get_user(self,domain)
whtml(self,"htmls/drmfree.html",domain=domain,user=user)
elif "it-is-what-it-is" in name:
user=get_user(self,domain)
whtml(self,"htmls/disclaimers.html",domain=domain,user=user)
elif "gallery/for/ideas" in name:
user=get_user(self,domain)
whtml(self,"utility/htmls/gallery.html",domain=domain,user=user,gallery=gallery)
else:
if domain.electron and self.request.get("buildid") in ["",None] and 0:
return self.response.out.write("<div style='color: gray'>Build expired</div>")
user=get_user(self,domain)
if not user and name.startswith("r/") and len(name.split("/"))>1:
referrer=name.split("/")[1]
referrer=get_by_iid("user|%s"%referrer)
if referrer:
logging.info("Referrer is %s"%referrer.k())
set_cookie(self,"referrer",referrer.k())
ip=get_ip_info(self); ip.referrer=referrer.k(); put_ip_info(ip)
return self.redirect("/")
if not user and name.startswith("c/") and len(name.split("/"))>1: #unused [19/11/18]
char=name.split("/")[1]
char=get_character(char)
if char:
logging.info("Referrer is %s"%char.owner)
set_cookie(self,"referrer",char.owner)
ip=get_ip_info(self); ip.referrer=char.owner; put_ip_info(ip)
if not domain.https_mode:
if not self.request.headers.get("Cf-Visitor") and self.request.scheme=="https" or self.request.headers.get("Cf-Visitor") and "https" in self.request.headers.get("Cf-Visitor"):
return self.redirect(self.request.url.replace("https","http"))
render_selection(self,user,domain)
def post(self,name):
self.get(name)
@ndb.toplevel
@app.route('/comm')
def serve_comm():
domain=gdi(request); user=get_user(request,domain)
return render_comm(request,user,domain)
class CommHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=gdi(self); user=get_user(self,domain)
render_comm(self,user,domain)
def post(self):
self.get()
@app.route('/character/<name>')
def serve_character(name=""):
domain=gdi(request); user=get_user(request,domain)
character=get_character(name)
if not character: return whtml(request,"htmls/simple_message.html",domain=domain,message="Not Found")
if not user:
ref=character.private and name or character.owner
logging.info("Referrer is %s"%ref)
set_cookie(request,"referrer",ref)
ip=get_ip_info(request); ip.referrer=ref; put_ip_info(ip)
domain.title=character.info.name
return whtml(request,"htmls/character.html",domain=domain,character=character)
class CharacterHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self,name):
domain=gdi(self); user=get_user(self,domain)
character=get_character(name)
if not character: return whtml(self,"htmls/simple_message.html",domain=domain,message="Not Found")
if not user:
ref=character.private and name or character.owner
logging.info("Referrer is %s"%ref)
set_cookie(self,"referrer",ref)
ip=get_ip_info(self); ip.referrer=ref; put_ip_info(ip)
domain.title=character.info.name
whtml(self,"htmls/character.html",domain=domain,character=character)
def post(self,name):
self.get(name)
@ndb.toplevel
@app.route('/characters')
def server_characters():
domain=gdi(request); user=get_user(request,domain)
characters=Character.query().order(-Character.level).fetch(500)
domain.title="Characters"
return whtml(request,"htmls/player.html",domain=domain,characters=characters)
class VCharacterHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self,name):
domain=gdi(self); user=get_user(self,domain)
character=get_character(name)
if not character: return whtml(self,"htmls/simple_message.html",domain=domain,message="Not Found")
characters=[character]
for c in Backup.query(Backup.backup_item_id==character.k('i')).order(-Backup.backup_created).fetch(80):
c.name=c.info.name="%s%s"%(c.info.name,(datetime.now()-c.backup_created).days)
logging.info(c.info.slots["mainhand"]["name"])
characters.append(c)
domain.title=character.info.name
whtml(self,"htmls/player.html",domain=domain,characters=characters)
def post(self,name):
self.get(name)
@app.route('/player/<name>')
def serve_player(name=""):
domain=gdi(request); user=get_user(request,domain)
character=get_character(name)
if not character: return whtml(request,"htmls/simple_message.html",domain=domain,message="Not Found")
player=get_by_iid("user|%s"%character.owner)
if not player: return whtml(request,"htmls/simple_message.html",domain=domain,message="Not Found")
if not user:
ref=character.private and name or player.k()
logging.info("Referrer is %s"%ref)
set_cookie(request,"referrer",ref)
ip=get_ip_info(request); ip.referrer=ref; put_ip_info(ip)
domain.title=player.name
characters=[]
if character.private: entities=[character]; domain.title=character.info.name
else: entities=Character.query(Character.owner==character.owner, Character.private==False).fetch()
if not len(entities): entities=[character]; domain.title=character.info.name
for c in gf(player,"characters",[]):
for e in entities:
if simplify_name(e.name)==simplify_name(c["name"]):
characters.append(e)
return whtml(request,"htmls/player.html",domain=domain,characters=characters)
class CharactersHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=gdi(self); user=get_user(self,domain)
characters=Character.query().order(-Character.level).fetch(500)
domain.title="Characters"
whtml(self,"htmls/player.html",domain=domain,characters=characters)
def post(self):
self.get()
@app.route('/merchants')
@ndb.toplevel
def serve_merchants():
domain=gdi(request); user=get_user(request,domain)
domain.title="All Online Merchants!"
entities=Character.query(Character.online == True, Character.type == "merchant").fetch()
return whtml(request,"htmls/player.html",domain=domain,characters=entities,merchants=True)
class PlayerHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self,name):
domain=gdi(self); user=get_user(self,domain)
character=get_character(name)
if not character: return whtml(self,"htmls/simple_message.html",domain=domain,message="Not Found")
player=get_by_iid("user|%s"%character.owner)
if not player: return whtml(self,"htmls/simple_message.html",domain=domain,message="Not Found")
if not user:
ref=character.private and name or player.k()
logging.info("Referrer is %s"%ref)
set_cookie(self,"referrer",ref)
ip=get_ip_info(self); ip.referrer=ref; put_ip_info(ip)
domain.title=player.name
characters=[]
if character.private: entities=[character]; domain.title=character.info.name
else: entities=Character.query(Character.owner==character.owner, Character.private==False).fetch()
if not len(entities): entities=[character]; domain.title=character.info.name
for c in gf(player,"characters",[]):
for e in entities:
if simplify_name(e.name)==simplify_name(c["name"]):
characters.append(e)
whtml(self,"htmls/player.html",domain=domain,characters=characters)
def post(self,name):
self.get(name)
@ndb.toplevel
@app.route('/character/<name>/in/<region>/<sname>')
@app.route('/character/<name>/in/<region>/<sname>/')
def serve_server_and_character_selection(name="",region="",sname=""):
logging.info("CMainHandler %s %s %s"%(name,region,sname))
domain=gdi(request); level=80
if domain.electron and request.values.get("buildid") in ["",None] and 0:
return request.response.set_data("<div style='color: gray'>Build expired</dov>")
if not domain.https_mode:
if not request.headers.get("Cf-Visitor") and request.scheme=="https" or request.headers.get("Cf-Visitor") and "https" in request.headers.get("Cf-Visitor"):
return request.redirect(request.url.replace("https","http"))
servers=get_servers()
user=get_user(request,domain)
code=request.values.get("code")
if code: domain.explicit_slot=code
for c in gf(user,"characters",[]):
if simplify_name(c["name"])==simplify_name(name):
domain.character_name=c["name"]
domain.url_character=c["id"]#so retry happens - this always gets set [05/01/18]
level=c["level"]
for server in servers:
if server.region==region and server.name==sname:
domain.url_ip=domain.https and server.ip or server.actual_ip
domain.url_port=server.port
logging.info("Set!")
return render_selection(request,user,domain,level=level)
class MerchantsHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=gdi(self); user=get_user(self,domain)
domain.title="All Online Merchants!"
entities=Character.query(Character.online == True, Character.type == "merchant").fetch()
whtml(self,"htmls/player.html",domain=domain,characters=entities,merchants=True)
def post(self):
self.get()
@ndb.toplevel
@app.route('/server/<region>/<sname>')
@app.route('/server/<region>/<sname>/')
def serve_server_selection(region="",sname=""):
logging.info("CMainHandler %s %s %s"%(name,region,sname))
domain=gdi(request); S=None
if domain.electron and request.values.get("buildid") in ["",None] and 0:
return request.response.set_data("<div style='color: gray'>Build expired</dov>")
if not domain.https_mode:
if not request.headers.get("Cf-Visitor") and request.scheme=="https" or request.headers.get("Cf-Visitor") and "https" in request.headers.get("Cf-Visitor"):
return request.redirect(request.url.replace("https","http"))
servers=get_servers()
user=get_user(request,domain)
for server in servers:
if server.region==region and server.name==sname:
S=server
return render_selection(request,user,domain,server=S)
class CMainHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self,name,region,sname):
logging.info("CMainHandler %s %s %s"%(name,region,sname))
domain=gdi(self); level=80
if domain.electron and self.request.get("buildid") in ["",None] and 0:
return self.response.out.write("<div style='color: gray'>Build expired</dov>")
@app.route('/ev/<uid>/<v>')
@ndb.toplevel
def server_everify(uid,v):
domain=gdi(request)
user=get_by_iid("user|%s"%uid)
logging.info(user)
message="Email Verification Failed"
if user and not gf(user,"verified"):
if gf(user,"everification")==v:
def verification_transaction():
element=get_by_iid(user.k('i'))
element.info.verified=True
element.put()
return element
if ndb.transaction(verification_transaction,xg=True,retries=8): message="Your Email Is Now Verified"
elif user:
message="Your Email Is Already Verified"
whtml(request,"htmls/simple_message.html",domain=domain,message=message)
return request.response
@app.route('/reset/<uid>/<key>')
@ndb.toplevel
def server_reset(uid,key):
domain=gdi(request)
user=get_by_iid("user|%s"%uid)
logging.info(user)
if user and gf(user,"password_key","123")==key:
whtml(request,"htmls/contents/password_reset.html",domain=domain,user=user,id=uid,key=key)
else:
message="Invalid Password Reset URL"
whtml(request,"htmls/simple_message.html",domain=domain,message=message)
return request.response
@app.route('/code.js',methods=['GET','POST'])
def serve_codejs():
domain=gdi(request); user=get_user(request,domain); code=None; name=request.values.get("name")
if user:
data=get_user_data(user)
for slot in gf(data,"code_list",{}):
if "%s"%slot=="%s"%name or ("%s"%data.info.code_list[slot][0]).lower()==("%s"%to_filename(name)).lower():
code=ndb.Key(InfoElement,"USERCODE-%s-%s"%(user.k(),slot)).get()
if code:
request.response.set_data("%s"%code.info.code)
return request.response
if request.values.get("xrequire"): request.response.set_data("throw('xrequire: Code not found')")
else: request.response.set_data("game_log('load_code: Code not found',colors.code_error)")
return request.response
@app.route('/data.js',methods=['GET','POST'])
@ndb.toplevel
def serve_datajs():
domain=gdi(request); additional=""
geometry={}
for name in maps:
key=maps[name]["key"]
if maps[name].get("ignore"): continue
#if name=="test" and not is_sdk: key="test"
#logging.info(key)
geometry[name]=get_by_iid("map|%s"%key).info.data
data={
"version":game_version,
"achievements":achievements,
"animations":animations,
"monsters":monsters,
"sprites":sprites,
"maps":maps,
"geometry":geometry,
"npcs":npcs,
"tilesets":tilesets,
"imagesets":imagesets,
"items":items,
"sets":sets,
"craft":craft,
"titles":titles,
"tokens":tokens,
"dismantle":dismantle,
"conditions":conditions,
"cosmetics":cosmetics,
"emotions":emotions,
"projectiles":projectiles,
"classes":classes,
"dimensions":dimensions,
"images":precomputed.images,
"levels":levels,
"positions":positions,
"skills":skills,
"events":events,
"games":games,
"multipliers":multipliers,
#"codes":codes,
"docs":docs,
"drops":drops,
}
if request.values.get("reload"): additional="add_log('Game data reloaded','#32A3B0');\napply_backup()\n"
request.response.set_data("var G=%s;\n%s"%(json.dumps(data),additional))
return request.response
@ndb.toplevel
@app.route('/shells')
def serve_shells():
if not request.headers.get("Cf-Visitor") and request.scheme=="http" or request.headers.get("Cf-Visitor") and "https" not in request.headers.get("Cf-Visitor"):
if not is_sdk: return request.redirect(request.url.replace("http","https"))
domain=gdi(request); user=get_user(request,domain)
#if security_threat(request,domain): return #commented out [23/04/20]
servers=get_servers(); server=select_server(request,user,servers)
domain.stripe_enabled=True
return whtml(request,"htmls/payments.html",domain=domain,user=user,server=server,extra_shells=extra_shells)
@app.route("/map/<name>",methods=['GET']) #Resort Map Editor
@app.route("/map/<name>/<suffix>",methods=['GET'])
def serve_resort_get(name="",suffix=""):
if not name: return
name=name.split("/")[0]
domain=gdi(request); user=get_user(request,domain)
if security_threat(request,domain): return
if not user or (not name.startswith("%s_"%user.k()) and user.k() not in inner_circle): return
number=name.split("_")[1]
if number not in ["1","2","3","4","5","6","7","8","9","10"]: return
map=get_by_iid("map|%s"%name)
mtilesets=copy.copy(tilesets)
if map and gf(map,"image_map"): mtilesets["this_map"]={"file":"/tileset/%s.png"%map.info.image_map.blob_key}
if map and gf(map,"image_map_a"): mtilesets["this_map_a"]={"file":"/tileset/%s.png"%map.info.image_map_a.blob_key}
if not mtilesets.get("this_map"): mtilesets["this_map"]={"file":"/images/tiles/map/resort_default.png"}
if not mtilesets.get("this_map_a"): mtilesets["this_map_a"]={"file":"/images/tiles/map/resort_default_a.png"}
upload_url=blobstore.create_upload_url('/upload/tileset',gs_bucket_name=domain.gcs_bucket+"/images")
return whtml(request,"utility/htmls/map_editor.html",domain=domain,name=name,map=map,upload_url=upload_url,tilesets=mtilesets,community=1,resort=1)
@app.route("/map/<name>",methods=['POST'])
@app.route("/map/<name>/<suffix>",methods=['POST'])
def serve_resort_post(name="",suffix=""):
data=request.values.get("data")
if not name: return
name=name.split("/")[0]
domain=gdi(request); user=get_user(request,domain)
if not user or not name.startswith("%s_"%user.k()): return
number=name.split("_")[1]
if number not in ["1","2","3","4","5","6","7","8","9","10"]: return
map=get_by_iid("map|%s"%name)
if not map: map=Map(key=ndb.Key(Map,name),info=GG())
map.info.data=json.loads(data)
process_map(map)
map.player=True
map.updated=datetime.now()
map.put()
return to_pretty_num(olen(map.info.data))
@app.route('/maps')
@app.route('/maps/<order>')
def serve_maps(order=""):
domain=gdi(request)
def map_to_html(m):
return "<div style='margin-bottom: 2px'><a href='%s/%s' target='_blank' style='color: white; font-weight: bold; text-decoration:none'>%s</a></div>"%(url,m.id(),m.id())
html="<style> html{background-color:gray}</style>"
if is_sdk: url="%s/admin/map_editor"%(domain.base_url)
else: url="%s/%s"%(domain.base_url,"communitymaps")
if order=="key":
for m in Map.query().fetch(5000,keys_only=True): html+=map_to_html(m)
else:
for m in Map.query().order(-Map.updated).fetch(5000,keys_only=True): html+=map_to_html(m)
return html
@app.route('/privacy')
def serve_privacy():
domain=gdi(request); user=get_user(request,domain)
return whtml(request,"htmls/page.html",domain=domain,user=user,content="privacy")
@app.route('/terms')
def serve_terms():
domain=gdi(request); user=get_user(request,domain)
return whtml(request,"htmls/page.html",domain=domain,user=user,content="terms")
@app.route('/contact')
def serve_contact():
domain=gdi(request); user=get_user(request,domain)
return whtml(request,"htmls/page.html",domain=domain,user=user,content="contact")
@app.route('/credits')
def serve_credits():
domain=gdi(request); user=get_user(request,domain)
return whtml(request,"htmls/page.html",domain=domain,user=user,content="credits")
@app.route('/docs')
@app.route('/docs/<path>')
def serve_docs(path=""):
domain=gdi(request); user=get_user(request,domain); domain.title="Docs"
if len(path):
domain.title="Docs /%s"%path
path=path.split("/")
return whtml(request,"htmls/docs.html",domain=domain,user=user,content="docs",dpath=path,extras=True)
@ndb.toplevel
@app.route('/rearm')
def serve_rearm():
if not is_sdk: return ""
free_servers()
unlock_all()
return "done!"
@ndb.toplevel
@app.route('/', methods=['POST', 'GET'])
@app.route('/<name>', methods=['POST', 'GET'])
def server_main(name=""):
domain=gdi(request)
if 0 and "treasurehunter" in name:
whtml(request,"examples/learningPixi/treasure_hunter.html")
elif "runner" in name:
whtml(request,"htmls/runner.html",domain=domain)
elif "executor" in name:
whtml(request,"htmls/executor.html",domain=domain)
elif name=="info/cookies":
request.response.set_data(repr(request.cookies))
elif "logs" in name:
user=get_user(request,domain)
whtml(request,"htmls/logs.html",domain=domain,user=user)
elif "linux" in name:
user=get_user(request,domain)
whtml(request,"htmls/linux.html",domain=domain,user=user)
elif "macos" in name:
user=get_user(request,domain)
whtml(request,"htmls/macos.html",domain=domain,user=user)
elif "allnotes" in name:
user=get_user(request,domain)
whtml(request,"htmls/allnotes.html",domain=domain,user=user)
elif "roadmap" in name:
user=get_user(request,domain)
whtml(request,"htmls/roadmap.html",domain=domain,user=user)
elif "drm-free" in name:
user=get_user(request,domain)
whtml(request,"htmls/drmfree.html",domain=domain,user=user)
elif "it-is-what-it-is" in name:
user=get_user(request,domain)
whtml(request,"htmls/disclaimers.html",domain=domain,user=user)
elif "gallery/for/ideas" in name:
user=get_user(request,domain)
whtml(request,"utility/htmls/gallery.html",domain=domain,user=user,gallery=gallery)
else:
if domain.electron and request.values.get("buildid") in ["",None] and 0:
request.response.set_data("<div style='color: gray'>Build expired</div>")
return request.response
user=get_user(request,domain)
if not user and name.startswith("r/") and len(name.split("/"))>1:
referrer=name.split("/")[1]
referrer=get_by_iid("user|%s"%referrer)
if referrer:
logging.info("Referrer is %s"%referrer.k())
set_cookie(request,"referrer",referrer.k())
ip=get_ip_info(request); ip.referrer=referrer.k(); put_ip_info(ip)
return request.response.redirect("/")
if not user and name.startswith("c/") and len(name.split("/"))>1: #unused [19/11/18]
char=name.split("/")[1]
char=get_character(char)
if char:
logging.info("Referrer is %s"%char.owner)
set_cookie(request,"referrer",char.owner)
ip=get_ip_info(request); ip.referrer=char.owner; put_ip_info(ip)
if not domain.https_mode:
if not self.request.headers.get("Cf-Visitor") and self.request.scheme=="https" or self.request.headers.get("Cf-Visitor") and "https" in self.request.headers.get("Cf-Visitor"):
return self.redirect(self.request.url.replace("https","http"))
servers=get_servers()
user=get_user(self,domain)
code=self.request.get("code")
if code: domain.explicit_slot=code
for c in gf(user,"characters",[]):
if simplify_name(c["name"])==simplify_name(name):
domain.character_name=c["name"]
domain.url_character=c["id"]#so retry happens - this always gets set [05/01/18]
level=c["level"]
for server in servers:
if server.region==region and server.name==sname:
domain.url_ip=domain.https and server.ip or server.actual_ip
domain.url_port=server.port
logging.info("Set!")
render_selection(self,user,domain,level=level)
def post(self,name,region,sname):
self.get(name,region,sname)
class SMainHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self,region,sname):
logging.info("CMainHandler %s %s %s"%(name,region,sname))
domain=gdi(self); S=None
if domain.electron and self.request.get("buildid") in ["",None] and 0:
return self.response.out.write("<div style='color: gray'>Build expired</dov>")
if not domain.https_mode:
if not self.request.headers.get("Cf-Visitor") and self.request.scheme=="https" or self.request.headers.get("Cf-Visitor") and "https" in self.request.headers.get("Cf-Visitor"):
return self.redirect(self.request.url.replace("https","http"))
servers=get_servers()
user=get_user(self,domain)
for server in servers:
if server.region==region and server.name==sname:
S=server
render_selection(self,user,domain,server=S)
def post(self,region,sname):
self.get(region,sname)
class EmailVerification(webapp.RequestHandler):
@ndb.toplevel
def get(self,uid,v):
domain=gdi(self)
user=get_by_iid("user|%s"%uid)
logging.info(user)
message="Email Verification Failed"
if user and not gf(user,"verified"):
if gf(user,"everification")==v:
def verification_transaction():
element=get_by_iid(user.k('i'))
element.info.verified=True
element.put()
return element
if ndb.transaction(verification_transaction,xg=True,retries=8): message="Your Email Is Now Verified"
elif user:
message="Your Email Is Already Verified"
whtml(self,"htmls/simple_message.html",domain=domain,message=message)
def post(self,uid,v):
self.get(uid,v)
class PasswordReset(webapp.RequestHandler):
@ndb.toplevel
def get(self,uid,key):
domain=gdi(self)
user=get_by_iid("user|%s"%uid)
logging.info(user)
if user and gf(user,"password_key","123")==key:
whtml(self,"htmls/contents/password_reset.html",domain=domain,user=user,id=uid,key=key)
else:
message="Invalid Password Reset URL"
whtml(self,"htmls/simple_message.html",domain=domain,message=message)
def post(self,uid,v):
self.get(uid,v)
class CodeJS(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=gdi(self); user=get_user(self,domain); code=None; name=self.request.get("name")
if user:
data=get_user_data(user)
for slot in gf(data,"code_list",{}):
if "%s"%slot=="%s"%name or ("%s"%data.info.code_list[slot][0]).lower()==("%s"%to_filename(name)).lower():
code=ndb.Key(InfoElement,"USERCODE-%s-%s"%(user.k(),slot)).get()
if code:
self.response.out.write("%s"%code.info.code)
return
if self.request.get("xrequire"): self.response.out.write("throw('xrequire: Code not found')")
else: self.response.out.write("game_log('load_code: Code not found',colors.code_error)")
def post(self):
self.get()
class DataJS(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=gdi(self); additional=""
geometry={}
for name in maps:
key=maps[name]["key"]
if maps[name].get("ignore"): continue
#if name=="test" and not is_sdk: key="test"
#logging.info(key)
geometry[name]=get_by_iid("map|%s"%key).info.data
data={
"version":game_version,
"achievements":achievements,
"animations":animations,
"monsters":monsters,
"sprites":sprites,
"maps":maps,
"geometry":geometry,
"npcs":npcs,
"tilesets":tilesets,
"imagesets":imagesets,
"items":items,
"sets":sets,
"craft":craft,
"titles":titles,
"tokens":tokens,
"dismantle":dismantle,
"conditions":conditions,
"cosmetics":cosmetics,
"emotions":emotions,
"projectiles":projectiles,
"classes":classes,
"dimensions":dimensions,
"images":precomputed.images,
"levels":levels,
"positions":positions,
"skills":skills,
"events":events,
"games":games,
"multipliers":multipliers,
#"codes":codes,
"docs":docs,
"drops":drops,
}
if self.request.get("reload"): additional="add_log('Game data reloaded','#32A3B0');\napply_backup()\n"
self.response.out.write("var G=%s;\n%s"%(json.dumps(data),additional))
def post(self):
self.get()
class PaymentsHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
if not self.request.headers.get("Cf-Visitor") and self.request.scheme=="http" or self.request.headers.get("Cf-Visitor") and "https" not in self.request.headers.get("Cf-Visitor"):
if not is_sdk: return self.redirect(self.request.url.replace("http","https"))
domain=gdi(self); user=get_user(self,domain)
#if security_threat(self,domain): return #commented out [23/04/20]
servers=get_servers(); server=select_server(self,user,servers)
domain.stripe_enabled=True
whtml(self,"htmls/payments.html",domain=domain,user=user,server=server,extra_shells=extra_shells)
def post(self):
self.get()
class TileUpload(blobstore_handlers.BlobstoreUploadHandler):
@ndb.toplevel
def post(self):
domain=gdi(self); user=get_user(self,domain)
if security_threat(self,domain): return
upload_files = self.get_file_infos('image'); file_info = upload_files[0]; blob_key=blobstore.create_gs_key_async(file_info.gs_object_name)
key,name=gdmul(self,"key","iname")
if name not in ["map","map_a"] or not user or not key.startswith(""+user.k()):
if 0: return jhtmle(self,"Permission issue")
else: return self.response.out.write("Permission issue")
logging.info("Image Upload %s %s"%(key,name))
map=get_by_iid("map|%s"%key); blob_key=blob_key.get_result()
if not map: map=Map(key=ndb.Key(Map,key),info=GG())
if file_info.content_type in ["image/jpg","image/jpeg","image/jpe","image/png","image/gif"]:
if gf(map,"image_%s"%name):
try: blobstore.delete(blob_keys=gf(map,"image_%s"%name).blob_key)
except: log_trace()
setattr(map.info,"image_%s"%name,cGG(blob_key=blob_key,
content_type=file_info.content_type,gs_object_name=file_info.gs_object_name))
if 0: jhtml(self,[{"type":"success","message":"Image uploaded"}])
else: self.redirect("/map/"+key)
map.put()
else:
blobstore.delete(blob_keys=blob_key)
if 0: jhtmle(self,"Not an image")
else: self.response.out.write("Not an image")
def get(self): self.post()
class GCSServeHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self,resource):
#resource = str(urllib.unquote(resource))
#blob_key=blobstore.create_gs_key(resource)
#self.send_blob(blob_key)
logging.info("here")
resource = str(urllib.unquote(resource))
self.send_blob(resource)
class ResortEditor(webapp.RequestHandler):
@ndb.toplevel
def get(self,name,suffix):
if not name: return
name=name.split("/")[0]
domain=gdi(self); user=get_user(self,domain)
if security_threat(self,domain): return
if not user or (not name.startswith("%s_"%user.k()) and user.k() not in inner_circle): return
number=name.split("_")[1]
if number not in ["1","2","3","4","5","6","7","8","9","10"]: return
map=get_by_iid("map|%s"%name)
mtilesets=copy.copy(tilesets)
if map and gf(map,"image_map"): mtilesets["this_map"]={"file":"/tileset/%s.png"%map.info.image_map.blob_key}
if map and gf(map,"image_map_a"): mtilesets["this_map_a"]={"file":"/tileset/%s.png"%map.info.image_map_a.blob_key}
if not mtilesets.get("this_map"): mtilesets["this_map"]={"file":"/images/tiles/map/resort_default.png"}
if not mtilesets.get("this_map_a"): mtilesets["this_map_a"]={"file":"/images/tiles/map/resort_default_a.png"}
upload_url=blobstore.create_upload_url('/upload/tileset',gs_bucket_name=domain.gcs_bucket+"/images")
whtml(self,"utility/htmls/map_editor.html",domain=domain,name=name,map=map,upload_url=upload_url,tilesets=mtilesets,community=1,resort=1)
def post(self,name,suffix):
data=self.request.get("data")
if not name: return
name=name.split("/")[0]
domain=gdi(self); user=get_user(self,domain)
if not user or not name.startswith("%s_"%user.k()): return
number=name.split("_")[1]
if number not in ["1","2","3","4","5","6","7","8","9","10"]: return
map=get_by_iid("map|%s"%name)
if not map: map=Map(key=ndb.Key(Map,name),info=GG())
map.info.data=json.loads(data)
process_map(map)
map.player=True
map.updated=datetime.now()
map.put()
self.response.out.write(to_pretty_num(olen(map.info.data)))
class Redirector(webapp.RequestHandler):
@ndb.toplevel
def get(self):
self.response.out.write("<script>window.top.location='http://adventure.land'</script> <!-- This is temporary, until I can set up the FB version [04/NOV/16] -->")
def post(self):
self.get()
class WarmupHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
pass
def post(self):
self.get()
class MapLister(webapp.RequestHandler):
@ndb.toplevel
def get(self,order):
domain=gdi(self)
def map_to_html(m):
return "<div style='margin-bottom: 2px'><a href='%s/%s' target='_blank' style='color: white; font-weight: bold; text-decoration:none'>%s</a></div>"%(url,m.id(),m.id())
html="<style> html{background-color:gray}</style>"
if is_sdk: url="%s/admin/map_editor"%(domain.base_url)
else: url="%s/%s"%(domain.base_url,"communitymaps")
if order=="key":
for m in Map.query().fetch(5000,keys_only=True): html+=map_to_html(m)
else:
for m in Map.query().order(-Map.updated).fetch(5000,keys_only=True): html+=map_to_html(m)
self.response.out.write(html)
def post(self):
self.get()
class IPLister(webapp.RequestHandler):
@ndb.toplevel
def get(self,order):
domain=gdi(self)
def ip_to_html(ip):
return "<div style='margin-bottom: 2px'>%s {%s} [%s]</div>"%(ip.info.explanation,ip.random_id,ip.info.limit)
html="<style> html{background-color:gray}</style>"
for ip in IP.query(IP.exception==True).fetch(5000): html+=ip_to_html(ip)
self.response.out.write(html)
def post(self):
self.get()
class PrivacyHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=gdi(self); user=get_user(self,domain)
whtml(self,"htmls/page.html",domain=domain,user=user,content="privacy")
class TermsHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=gdi(self); user=get_user(self,domain)
whtml(self,"htmls/page.html",domain=domain,user=user,content="terms")
class ContactHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=gdi(self); user=get_user(self,domain)
whtml(self,"htmls/page.html",domain=domain,user=user,content="contact")
class DocsHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self,path):
domain=gdi(self); user=get_user(self,domain); domain.title="Docs"
if len(path):
domain.title="Docs /%s"%path
path=path.split("/")
whtml(self,"htmls/docs.html",domain=domain,user=user,content="docs",dpath=path,extras=True)
class CreditsHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=gdi(self); user=get_user(self,domain)
whtml(self,"htmls/page.html",domain=domain,user=user,content="credits")
class RearmHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
if not is_sdk: return
free_servers()
unlock_all()
application = webapp.WSGIApplication([
('/ev/(.*)/([^/]*)/?', EmailVerification),
('/reset/(.*)/([^/]*)/?', PasswordReset),
('/data.js', DataJS),
('/code.js', CodeJS),
('/upload/tileset.*',TileUpload),
('/tileset/(.*)\.png',GCSServeHandler),
('/maps/?(.*)',MapLister),
('/ips/?(.*)',IPLister),
('/docs/?(.*)',DocsHandler),
('/map/(.*)/?(.*)',ResortEditor),
('/communitymaps/?(.*)', CommunityMapEditor),
('/editmap/?(.*)', ArtistMapEditor),
('/communityselector/?(.*)', Selector),
('/communityitems', Items),
('/shells', PaymentsHandler),
('/temporaryfbcanvas', Redirector),
('/_ah/warmup',WarmupHandler),
('/privacy.*',PrivacyHandler),
('/terms.*',TermsHandler),
('/contact.*',ContactHandler),
('/credits.*',CreditsHandler),
('/server/([^/]*)/([^/]*)/?', SMainHandler),
('/character/([^/]*)/in/([^/]*)/([^/]*)/?', CMainHandler),
('/character/([^/]*)/?', CharacterHandler),
('/visualizeT2/([^/]*)/?', VCharacterHandler),
('/player/([^/]*)/?', PlayerHandler),
('/characters', CharactersHandler),
('/merchants', MerchantsHandler),
('/comm/?.*', CommHandler),
('/rearm',RearmHandler),
('/?(.*)', MainHandler),
],debug=is_sdk)
if not request.headers.get("Cf-Visitor") and request.scheme=="https" or request.headers.get("Cf-Visitor") and "https" in request.headers.get("Cf-Visitor"):
return request.redirect(request.url.replace("https","http"))
render_selection(request,user,domain)
return request.response
+16
View File
@@ -0,0 +1,16 @@
Install google-cloud-sdk from: https://cloud.google.com/sdk/docs/install
Replace app.yaml with the python3 one
python3 CLOUD_SDK_ROOT/bin/dev_appserver.py OPTION yaml_path FILES
The python3 dev_appserver uses Java objects, test it according to your needs and performance expectations, I personally favor Python2 still [27/01/24]
Useful: https://cloud.google.com/appengine/docs/standard/tools/local-devserver-command?tab=python
alias run_thegame3="python3.11 /Users/kaan/Desktop/APPENGINE/google-cloud-sdk/bin/dev_appserver.py --storage_path=/Users/kaan/Desktop/DEPLOY/python3/storage/ --blobstore_path=/Users/kaan/Desktop/DEPLOY/python3/storage/blobstore/ --datastore_path=/Users/kaan/Desktop/DEPLOY/python3/storage/new_db.bin --host=0.0.0.0 --port=8083 /Users/kaan/Desktop/DEPLOY/python3/thegame/ --require_indexes --enable_host_checking=False"
Suggestions:
1) Invoke /admin/make with your email put in admin.py
2) From /admin/executor - call empty_fill_maps()
3) After getting rid of the initial errors, figure out the rest
+34
View File
@@ -0,0 +1,34 @@
version: 4 #2-new UI | 3-new font dynamics | 4-python3
runtime: python312
app_engine_apis: true
threadsafe: true
api_version: 1
builtins:
- deferred: on
#F2/F4: same warmup handler performance [14/10/14]
instance_class: F2
automatic_scaling:
min_idle_instances: 0
max_idle_instances: 1
min_pending_latency: 6000ms
max_pending_latency: 12000ms
default_expiration: "4d"
handlers:
- url: /favicon\.ico
static_files: images/favicon.ico
upload: favicon\.ico
- url: /css
static_dir: css
- url: /images
static_dir: images
- url: /img/
static_dir: images/screenshots
- url: /sounds
static_dir: sounds
- url: /js
static_dir: js
- url: .*
script: auto
+3
View File
@@ -0,0 +1,3 @@
Flask
lxml
appengine-python-standard
+1
View File
@@ -0,0 +1 @@
Flask
+24
View File
@@ -0,0 +1,24 @@
// creates a python3 project to safely test a python3 environment [27/01/24]
var path=require("path"),f=require(path.resolve(__dirname, "functions.js"));
f.execs("rm -rf ~/deploy/python3/thegame");
f.execs("mkdir ~/deploy/python3/thegame");
//f.execs("cp -r ~/thegame/* ~/deploy/python3/thegame");
f.execs("rsync -rv --exclude=.electron ~/thegame/* ~/deploy/python3/thegame"); // steamworkds_sdk is problematic to cp [11/02/19]
f.execs("rm -rf ~/deploy/python3/thegame/node");
f.execs("rm -rf ~/deploy/python3/thegame/scripts");
f.execs("cp -f ~/deploy/python3/thegame/python3/app.yaml ~/deploy/python3/thegame/app.yaml");
f.execs("cp -f ~/deploy/python3/thegame/python3/requirements.txt ~/deploy/python3/thegame/requirements.txt");
f.execs("rm -rf ~/deploy/python3/thegame/lib");
f.execs("mkdir ~/deploy/python3/thegame/lib");
f.execs("rm -rf ~/deploy/python3/thegame/origin");
f.execs("rm -rf ~/deploy/python3/thegame/electron");
f.execs("find ~/deploy/python3/thegame/ -name '*.pxm' -delete");
f.execs("cp ~/deploy/python3/thegame/js/runner_functions.js ~/deploy/python3/thegame/htmls/contents/codes/runner_functions.js");
f.execs("cp ~/deploy/python3/thegame/js/runner_compat.js ~/deploy/python3/thegame/htmls/contents/codes/runner_compat.js");
f.execs("cp ~/deploy/python3/thegame/js/common_functions.js ~/deploy/python3/thegame/htmls/contents/codes/common_functions.js");
// f.write_file("~/deploy/python3/thegame/app.yaml",f.read_file("~/thegame/app.yaml").replace("application:","#application:").replace("version:","#version:"));
+5 -1
View File
@@ -20,6 +20,10 @@ f.execs("cp ~/deploy/thegame_appengine/stack/update_notes.txt ~/deploy/thegame_a
f.execs("rm -rf ~/deploy/thegame_appengine/stack");
f.execs("mkdir ~/deploy/thegame_appengine/stack");
f.execs("cp ~/deploy/thegame_appengine/design/update_notes.txt ~/deploy/thegame_appengine/stack/update_notes.txt");
f.execs("cp -f ~/deploy/thegame_appengine/python3/app.yaml ~/deploy/thegame_appengine/app.yaml");
f.execs("cp -f ~/deploy/thegame_appengine/python3/requirements.txt ~/deploy/thegame_appengine/requirements.txt");
f.execs("rm -rf ~/deploy/thegame_appengine/lib");
f.execs("rm -rf ~/deploy/thegame_appengine/python3");
f.execs("rm -rf ~/deploy/thegame_appengine/origin");
f.execs("rm -rf ~/deploy/thegame_appengine/electron");
f.execs("find ~/deploy/thegame_appengine/ -name '*.pxm' -delete");
@@ -28,7 +32,7 @@ f.execs("cp ~/deploy/thegame_appengine/js/runner_functions.js ~/deploy/thegame_a
f.execs("cp ~/deploy/thegame_appengine/js/runner_compat.js ~/deploy/thegame_appengine/htmls/contents/codes/runner_compat.js");
f.execs("cp ~/deploy/thegame_appengine/js/common_functions.js ~/deploy/thegame_appengine/htmls/contents/codes/common_functions.js");
f.write_file("~/deploy/thegame_appengine/app.yaml",f.read_file("~/thegame/app.yaml").replace("application:","#application:").replace("version:","#version:"));
// f.write_file("~/deploy/thegame_appengine/app.yaml",f.read_file("~/thegame/app.yaml").replace("application:","#application:").replace("version:","#version:"));
to_minify={
// "js":["game.js","keyboard.js","html.js","functions.js","common_functions.js"],
+28 -61
View File
@@ -1,65 +1,32 @@
from config import *
from functions import *
class DownloadHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self):
password,iid=gdmul(self,"password","iid")
if password!=ELEMENT_PASSWORD: logging.error("breach attempt"); return
element=get_by_iid(iid)
self.response.out.write(cPickle.dumps(element))
def post(self): self.get()
@app.route('/tasks/download')
@ndb.toplevel
def serve_map_download(request):
password,iid=gdmul(request,"password","iid")
if password!=ELEMENT_PASSWORD: logging.error("breach attempt"); return
element=get_by_iid(iid)
return cPickle.dumps(element)
class PostTaskHandler(webapp.RequestHandler):
@ndb.toplevel
def post(self):
domain=gdi(self)
password=gdmul(self,"p")
if password==SDK_UPLOAD_PASSWORD:
logging.info("received a post_task")
task_code,obj1,obj2,obj3=gdmul(self,"task_code","obj1","obj2","obj3"); element=None; element2=None; result=""
if obj1: element=cPickle.loads(str(obj1))
if obj2: element2=cPickle.loads(str(obj2))
if obj3: element2=cPickle.loads(str(obj3))
exec(task_code)
logging.info(result)
self.response.headers.add_header('Content-Type','application/x-www-form-urlencoded')
if self.response.out.body and not result:
result=self.response.out.body
self.response.out.clear()
self.response.out.write(cPickle.dumps(result))
def get(self): self.post()
class PaymentwallHandler(webapp.RequestHandler):
@ndb.toplevel
def post(self):
domain=gdi(self)
self.response.out.write("1")
def get(self): self.post()
class SuperRewardsHandler(webapp.RequestHandler):
@ndb.toplevel
def post(self):
domain=gdi(self)
id,uid,oid,new,total,sig=gdmul(self,"id","uid","oid","new","total","sig")
logging.info("super_rewards %s"%([id,uid,oid,new,total,sig]))
if not marker_check("superrewards-%s-%s"%(id,uid)):
add_event(None,"superrewards_repeated",[],info=cGG(message="Super Rewards: callback repeated %s %s %s"%(uid,id,new),id=id,uid=uid,oid=oid,new=new,total=total,sig=sig))
self.response.out.write("1")
return
user=get_by_iid("user|%s"%uid)
if not user:
add_event(None,"superrewards_no_user",[],info=cGG(message="Super Rewards: user not found for %s %s %s"%(uid,id,new),id=id,uid=uid,oid=oid,new=new,total=total,sig=sig))
self.response.out.write("4") #http://docs.superrewards.com/docs/notification-postbacks
return
add_event(user,"superrewards_cash",["cashflow","payments"],info=cGG(message="Super Rewards: %s received %s shells"%(user.name,new),id=id,uid=uid,oid=oid,new=new,total=total,sig=sig))
add_cash(user,int(math.ceil(int(new))),referrer=1)
self.response.out.write("1")
def get(self): self.post()
application = webapp.WSGIApplication([
('/tasks/download.*', DownloadHandler),
('/tasks/post.*', PostTaskHandler),
('/tasks/pwallping.*', PaymentwallHandler),
('/tasks/srcallback.*', SuperRewardsHandler),
],debug=is_sdk)
@app.route('/tasks/post',methods=['GET','POST'])
@ndb.toplevel
def serve_post(request):
domain=gdi(request)
password=gdmul(request,"p")
if password==SDK_UPLOAD_PASSWORD:
logging.info("received a post_task")
task_code,obj1,obj2,obj3=gdmul(request,"task_code","obj1","obj2","obj3"); element=None; element2=None; result=""
if obj1: element=cPickle.loads(str(obj1))
if obj2: element2=cPickle.loads(str(obj2))
if obj3: element2=cPickle.loads(str(obj3))
exec(task_code)
logging.info(result)
request.response.headers.add_header('Content-Type','application/x-www-form-urlencoded')
if request.response.out.body and not result:
result=request.response.out.body
request.response.out.clear()
request.response.set_data(cPickle.dumps(result))
return request.response
else:
return "password_issue"
+32 -27
View File
@@ -1,31 +1,36 @@
from config import *
from functions import *
class MainHandler(webapp.RequestHandler):
@ndb.toplevel
def get(self,name):
domain=gdi(self)
if name=="hash":
start=datetime.now()
logging.info(pbkdf2_hex("test","123456",iterations=160))
logging.info(mssince(start))
elif name=="levels":
xp=200
for i in xrange(100):
new_xp=xp
while new_xp>=100: new_xp/=10
new_xp=int(new_xp)
while new_xp*10<=xp: new_xp*=10
xp=new_xp
self.response.out.write("level %s %sxp %s worms %s 40K's<br />"%((i+1),xp,xp/200.0,xp/40000.0))
xp*=1.25
elif name.startswith("pixi_"):
whtml(self,"utility/htmls/tests/%s.html"%name,domain=domain)
else:
self.response.out.write("test")
def post(self,name):
self.get(name)
def test():
@after_this_request
def add_header(response):
response.headers['X-Foo'] = 'Parachute'
return response
application = webapp.WSGIApplication([
('/test/?(.*)', MainHandler)
],debug=is_sdk)
@app.route('/test', methods=['POST', 'GET'])
@app.route('/test/<name>', methods=['POST', 'GET'])
@ndb.toplevel
def serve_test(name=""):
domain=gdi(request)
if name=="hash":
start=datetime.now()
logging.info(pbkdf2_hex("test","123456",iterations=160))
logging.info(mssince(start))
elif name=="levels":
xp=200
for i in xrange(100):
html=""
new_xp=xp
while new_xp>=100: new_xp/=10
new_xp=int(new_xp)
while new_xp*10<=xp: new_xp*=10
xp=new_xp
html+="level %s %sxp %s worms %s 40K's<br />"%((i+1),xp,xp/200.0,xp/40000.0)
xp*=1.25
return html
elif name.startswith("pixi_"):
return shtml("utility/htmls/tests/%s.html"%name,domain=domain)
else:
request.response=make_response('Setting the cookie')
#test()
return request.response
+94
View File
@@ -0,0 +1,94 @@
from config import *
from functions import *
# for Python2 [27/01/24]
class PhotoUpload(blobstore_handlers.BlobstoreUploadHandler):
@ndb.toplevel
def post(self):
domain=get(request); user=get_user(self,domain)
if security_threat(self,domain): return
if not getattr(user,"admin",False): return
upload_files = self.get_file_infos('image'); file_info = upload_files[0]; blob_key=blobstore.create_gs_key_async(file_info.gs_object_name)
key=gdmul(request,"key");
logging.info("Image Upload %s"%(key))
upload=get_by_iid("upload|%s"%key); blob_key=blob_key.get_result()
if not upload: upload=Upload(key=ndb.Key(Upload,key),info=GG())
if file_info.content_type in ["image/jpg","image/jpeg","image/jpe","image/png","image/gif"]:
if gf(upload,"blob_key"):
try:
blobstore.delete(blob_keys=upload.info.blob_key)
logging.info("Old image deleted")
except: log_trace()
url=get_serving_url(blob_key,size=images.IMG_SERVING_SIZES_LIMIT)
size=get_image_size(blob_key,encoding=filetype_to_image_encoding(file_info.content_type))
upload.info.blob_key=blob_key
upload.info.content_type=file_info.content_type
upload.info.gs_object_name=file_info.gs_object_name
upload.info.url=url
upload.info.width=size.width
upload.info.height=size.height
self.response.out.write("{<br/>&nbsp;'%s':{'url':'%s','width':%s,'height':%s},<br />}"%(key,url,size.width,size.height))
upload.put()
else:
blobstore.delete(blob_keys=blob_key)
self.response.out.write("Not an image")
def get(self): self.post()
class PhotoUploader(webapp.RequestHandler):
@ndb.toplevel
def get(self):
domain=get(request); user=get_user(self,domain)
if security_threat(self,domain): return
upload_url=blobstore.create_upload_url('/admin/upload/handler',gs_bucket_name=domain.gcs_bucket+"/files")
whtml(request,"utility/htmls/uploader.html",domain=domain,upload_url=upload_url)
class TileUpload(blobstore_handlers.BlobstoreUploadHandler):
@ndb.toplevel
def post(request):
domain=gdi(request); user=get_user(request,domain)
if security_threat(request,domain): return
upload_files = request.get_file_infos('image'); file_info = upload_files[0]; blob_key=blobstore.create_gs_key_async(file_info.gs_object_name)
key,name=gdmul(request,"key","iname")
if name not in ["map","map_a"] or not user or not key.startswith(""+user.k()):
if 0: return jhtmle(request,"Permission issue")
else: return request.response.set_data("Permission issue")
logging.info("Image Upload %s %s"%(key,name))
map=get_by_iid("map|%s"%key); blob_key=blob_key.get_result()
if not map: map=Map(key=ndb.Key(Map,key),info=GG())
if file_info.content_type in ["image/jpg","image/jpeg","image/jpe","image/png","image/gif"]:
if gf(map,"image_%s"%name):
try: blobstore.delete(blob_keys=gf(map,"image_%s"%name).blob_key)
except: log_trace()
setattr(map.info,"image_%s"%name,cGG(blob_key=blob_key,
content_type=file_info.content_type,gs_object_name=file_info.gs_object_name))
if 0: jhtml(request,[{"type":"success","message":"Image uploaded"}])
else: request.redirect("/map/"+key)
map.put()
else:
blobstore.delete(blob_keys=blob_key)
if 0: jhtmle(request,"Not an image")
else: request.response.set_data("Not an image")
def get(): request.post()
class GCSServeHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(resource):
#resource = str(urllib.unquote(resource))
#blob_key=blobstore.create_gs_key(resource)
#request.send_blob(blob_key)
logging.info("here")
resource = str(urllib.unquote(resource))
request.send_blob(resource)
application = webapp.WSGIApplication([
('/admin/upload', PhotoUploader),
('/admin/upload/handler', PhotoUpload),
('/upload/tileset.*',TileUpload),
('/tileset/(.*)\.png',GCSServeHandler),
],debug=is_sdk)
+1 -1
View File
@@ -61,7 +61,7 @@
</div>
<script>
var codemirror = CodeMirror(function(current){$("#code").replaceWith(current);},{
value: "domain=gdi(self); user=get_user(self,domain); user_data=get_user_data(user)\n",
value: "domain=gdi(request); user=get_user(request,domain); user_data=get_user_data(user)\n",
mode: "python",
indentUnit:4,
indentWithTabs:true,