def set_item(dbpath, uid, cid, data): obj = {'id': data['id']} obj['modified'] = round(time.time(), 2) obj['payload'] = data.get('payload', None) obj['payload_size'] = len(obj['payload']) if obj['payload'] else 0 obj['sortindex'] = data.get('sortindex', None) obj['parentid'] = data.get('parentid', None) obj['predecessorid'] = data.get('predecessorid', None) obj['ttl'] = data.get('ttl', None) if obj['sortindex']: try: obj['sortindex'] = int(math.floor(float(obj['sortindex']))) except ValueError: return obj with sqlite3.connect(dbpath) as db: sql = ('main.%s (id VARCHAR(64) PRIMARY KEY, modified FLOAT,' 'sortindex INTEGER, payload VARCHAR(256),' 'payload_size INTEGER, parentid VARCHAR(64),' 'predecessorid VARCHAR(64), ttl INTEGER)') % cid db.execute("CREATE table IF NOT EXISTS %s;" % sql) into = [] values = [] for k, v in iteritems(obj): into.append(k) values.append(v) try: db.execute("INSERT INTO %s (%s) VALUES (%s);" % \ (cid, ', '.join(into), ','.join(['?' for x in values])), values) except sqlite3.IntegrityError: for k, v in iteritems(obj): if v is None: continue db.execute('UPDATE %s SET %s=? WHERE id=?;' % (cid, k), [v, obj['id']]) except sqlite3.InterfaceError: raise ValueError return obj
def set_item(dbpath, uid, cid, data): obj = {'id': data['id']} obj['modified'] = round(time.time(), 2) obj['payload'] = data.get('payload', None) obj['payload_size'] = len(obj['payload']) if obj['payload'] else 0 obj['sortindex'] = data.get('sortindex', None) obj['parentid'] = data.get('parentid', None) obj['predecessorid'] = data.get('predecessorid', None) obj['ttl'] = data.get('ttl', None) if obj['sortindex']: try: obj['sortindex'] = int(math.floor(float(obj['sortindex']))) except ValueError: return obj with sqlite3.connect(dbpath) as db: sql = ('main.%s (id VARCHAR(64) PRIMARY KEY, modified FLOAT,' 'sortindex INTEGER, payload VARCHAR(256),' 'payload_size INTEGER, parentid VARCHAR(64),' 'predecessorid VARCHAR(64), ttl INTEGER)') % cid db.execute("CREATE table IF NOT EXISTS %s;" % sql) into = []; values = [] for k,v in iteritems(obj): into.append(k); values.append(v) try: db.execute("INSERT INTO %s (%s) VALUES (%s);" % \ (cid, ', '.join(into), ','.join(['?' for x in values])), values) except sqlite3.IntegrityError: for k,v in iteritems(obj): if v is None: continue db.execute('UPDATE %s SET %s=? WHERE id=?;' % (cid, k), [v, obj['id']]) except sqlite3.InterfaceError: raise ValueError return obj
def collection(app, environ, request, version, uid, cid): """/<float:version>/<username>/storage/<collection>""" if request.method == 'HEAD' or request.authorization.username != uid: return Response('Not Authorized', 401) dbpath = app.dbpath(uid, request.authorization.password) expire(dbpath, cid) ids = request.args.get('ids', None) offset = request.args.get('offset', None) older = request.args.get('older', None) newer = request.args.get('newer', None) full = request.args.get('full', False) index_above = request.args.get('index_above', None) index_below = request.args.get('index_below', None) limit = request.args.get('limit', None) offset = request.args.get('offset', None) sort = request.args.get('sort', None) parentid = request.args.get('parentid', None) predecessorid = request.args.get('predecessorid', None) try: older and float(older) newer and float(newer) limit and int(limit) offset and int(offset) index_above and int(index_above) index_below and int(index_below) except ValueError: raise BadRequest if limit is not None: limit = int(limit) if offset is not None: # we need both if limit is None: offset = None else: offset = int(offset) if not full: fields = ['id'] else: fields = FIELDS # filters used in WHERE clause filters = {} if ids is not None: filters['id'] = 'IN', '(%s)' % ids if older is not None: filters['modified'] = '<', float(older) if newer is not None: filters['modified'] = '>', float(newer) if index_above is not None: filters['sortindex'] = '>', int(index_above) if index_below is not None: filters['sortindex'] = '<', int(index_below) if parentid is not None: filters['parentid'] = '=', "'%s'" % parentid if predecessorid is not None: filters['predecessorid'] = '=', "'%s'" % predecessorid filter_query, sort_query, limit_query = '', '', '' # ORDER BY x ASC|DESC if sort is not None: if sort == 'index': sort_query = ' ORDER BY sortindex DESC' elif sort == 'oldest': sort_query = ' ORDER BY modified ASC' elif sort == 'newest': sort_query = ' ORDER BY modified DESC' # WHERE x<y AND ... if filters: filter_query = ' WHERE ' filter_query += ' AND '.join([k + ' ' + v[0] + ' ' + str(v[1]) for k, v in iteritems(filters)]) # LIMIT x [OFFSET y] if limit: limit_query += ' LIMIT %i' % limit if offset: limit_query += ' OFFSET %i' % offset if request.method == 'GET': # Returns a list of the WBO or ids contained in a collection. with sqlite3.connect(dbpath) as db: try: res = db.execute('SELECT %s FROM %s' % (','.join(fields), cid) \ + filter_query + sort_query + limit_query).fetchall() except sqlite3.OperationalError: res = [] res = [v[0] if len(fields) == 1 else wbo2dict(v) for v in res] res, mime, records = convert(res, request.accept_mimetypes.best) return Response(res, 200, content_type=mime, headers={'X-Weave-Records': str(records)}) # before we write, check if the data has not been modified since the request since = request.headers.get('X-If-Unmodified-Since', None) if since and has_modified(float(since), dbpath, cid): raise PreconditionFailed if request.method == 'DELETE': try: with sqlite3.connect(dbpath) as db: select = 'SELECT id FROM %s' % cid + filter_query \ + sort_query + limit_query db.execute('DELETE FROM %s WHERE id IN (%s)' % (cid, select)) except sqlite3.OperationalError: pass return Response(json.dumps(time.time()), 200) elif request.method in ('PUT', 'POST'): data = request.get_json() if isinstance(data, dict): data = [data] success, failed = [], [] for item in data: if 'id' not in item: failed.append(item) continue try: o = set_item(dbpath, uid, cid, item) success.append(o['id']) except ValueError: failed.append(item['id']) js = json.dumps({'modified': round(time.time(), 2), 'success': success, 'failed': failed}) return Response(js, 200, content_type='application/json', headers={'X-Weave-Timestamp': round(time.time(), 2)})
def collection(app, environ, request, version, uid, cid): """/<float:version>/<username>/storage/<collection>""" if request.method == 'HEAD' or request.authorization.username != uid: return Response('Not Authorized', 401) dbpath = app.dbpath(uid, request.authorization.password) expire(dbpath, cid) ids = request.args.get('ids', None) offset = request.args.get('offset', None) older = request.args.get('older', None) newer = request.args.get('newer', None) full = request.args.get('full', False) index_above = request.args.get('index_above', None) index_below = request.args.get('index_below', None) limit = request.args.get('limit', None) offset = request.args.get('offset', None) sort = request.args.get('sort', None) parentid = request.args.get('parentid', None) predecessorid = request.args.get('predecessorid', None) try: older and float(older) newer and float(newer) limit and int(limit) offset and int(offset) index_above and int(index_above) index_below and int(index_below) except ValueError: raise BadRequest if limit is not None: limit = int(limit) if offset is not None: # we need both if limit is None: offset = None else: offset = int(offset) if not full: fields = ['id'] else: fields = FIELDS # filters used in WHERE clause filters = {} if ids is not None: filters['id'] = 'IN', '(%s)' % ",".join("'" + x.strip() + "'" for x in ids.split(",")) if older is not None: filters['modified'] = '<', float(older) if newer is not None: filters['modified'] = '>', float(newer) if index_above is not None: filters['sortindex'] = '>', int(index_above) if index_below is not None: filters['sortindex'] = '<', int(index_below) if parentid is not None: filters['parentid'] = '=', "'%s'" % parentid if predecessorid is not None: filters['predecessorid'] = '=', "'%s'" % predecessorid filter_query, sort_query, limit_query = '', '', '' # ORDER BY x ASC|DESC if sort is not None: if sort == 'index': sort_query = ' ORDER BY sortindex DESC' elif sort == 'oldest': sort_query = ' ORDER BY modified ASC' elif sort == 'newest': sort_query = ' ORDER BY modified DESC' # WHERE x<y AND ... if filters: filter_query = ' WHERE ' filter_query += ' AND '.join( [k + ' ' + v[0] + ' ' + str(v[1]) for k, v in iteritems(filters)]) # LIMIT x [OFFSET y] if limit: limit_query += ' LIMIT %i' % limit if offset: limit_query += ' OFFSET %i' % offset if request.method == 'GET': # Returns a list of the WBO or ids contained in a collection. with sqlite3.connect(dbpath) as db: try: res = db.execute('SELECT %s FROM %s' % (','.join(fields), cid) \ + filter_query + sort_query + limit_query).fetchall() except sqlite3.OperationalError: res = [] res = [v[0] if len(fields) == 1 else wbo2dict(v) for v in res] res, mime, records = convert(res, request.accept_mimetypes.best) return Response(res, 200, content_type=mime, headers={'X-Weave-Records': str(records)}) # before we write, check if the data has not been modified since the request since = request.headers.get('X-If-Unmodified-Since', None) if since and has_modified(float(since), dbpath, cid): raise PreconditionFailed if request.method == 'DELETE': try: with sqlite3.connect(dbpath) as db: select = 'SELECT id FROM %s' % cid + filter_query \ + sort_query + limit_query db.execute('DELETE FROM %s WHERE id IN (%s)' % (cid, select)) except sqlite3.OperationalError: pass return Response(json.dumps(time.time()), 200) elif request.method in ('PUT', 'POST'): data = request.get_json() if isinstance(data, dict): data = [data] success, failed = [], [] for item in data: if 'id' not in item: failed.append(item) continue try: o = set_item(dbpath, uid, cid, item) success.append(o['id']) except ValueError: failed.append(item['id']) js = json.dumps({ 'modified': round(time.time(), 2), 'success': success, 'failed': failed }) return Response(js, 200, content_type='application/json', headers={'X-Weave-Timestamp': round(time.time(), 2)})