def delete(self, account_id, article_id): query = Message.query.join(Account).filter(Account.id == account_id) if account_id and not article_id: if 'article-id' not in request.json: abort(404) ids = get_article_ids(request.json['article-id']) count = len(ids) query = query.filter(Message.article_id.in_(ids)) else: query = query.filter(Message.article_id == article_id) count = 1 messages = query.all() if len(messages) > 0 and not messages[0].account.writable: abort(403, 'Account is not writable') if len(messages) != count: return make_response(jsonify({'error': 'Message not found'}), 404) sync = get_sync(Account.query.get(account_id)) for msg in messages: resp = sync.remote_delete(msg.google_id) if resp: click.echo('remote update failed') abort(resp[0], resp[1]), db.session().delete(msg) try: db.session().commit() except exc.SQLAlchemyError as ex: abort(500, ex) return jsonify({'result': True})
def delete(self, thread_id): thread = Thread.query.get(thread_id) if not thread: return make_response(jsonify({'error': 'Thread not found'}), 404) db.session().delete(thread) db.session().commit() return jsonify({'result': True})
def delete(self, account_id): account = Account.query.get(account_id) if not account: return make_response(jsonify({'error': 'Account not found'}), 404) try: db.session().delete(account) db.session().commit() except exc.SQLAlchemyError as ex: abort(500, ex) return jsonify({'result': True})
def delete(self, account_id, label_id): label = Label.query.get_or_404(label_id) if not label.account.writable: abort(403, 'Account is not writable') try: db.session().delete(label) db.session().commit() except exc.SQLAlchemyError as ex: abort(500, ex) return jsonify({'result': True})
def update(self, msgs): """Update a message in the database. Only message labels are allowed to change. We assume all messages are already in the database. This function is rather ineffcient. """ if not msgs: return if '__getitem__' not in dir(msgs): msgs = (msgs, ) session = db.session() objs = self.find_by_gid([m['id'] for m in msgs]) ohash = dict([(o.google_id, o) for o in objs]) assert len(objs) == len(msgs) for msg in msgs: obj = ohash[msg['id']] if 'labelIds' in msg: obj.labels = [self.get_label(lgid) for lgid in msg['labelIds']] else: obj.labels = [] obj.modified = datetime.now() logger.info('%s: update: (%d, %d) -> %s' % (self.options.nickname, obj.id, obj.article_id, ', '.join(obj.label_names))) session.commit()
def sync(client): config_file = os.path.join(TEST_DIR, 'test-config.yaml') try: config = yaml.load(open(config_file, mode='rb')) except IOError as ex: print('Error: reading config file (%s)' % config_file) raise ex email = '*****@*****.**' nickname = 'itineris' Account.as_unique(db.session(), email=email, nickname=nickname, writable=False, can_send=False) db.session().commit() sync = GmSync(email, nickname, config) yield sync
def __set_kv(self, key, value, upsert=False): """Store a key, value tuple in the database.""" session = db.session() kv = self.account.key_value.filter_by(key=key).first() if upsert and kv: ## Store the history of history_id for debugging purposes kv.value = value else: kv = KeyValue(account=self.account, key=key, value=value) session.add(kv) session.commit()
def put(self, account_id): account = Account.query.get(account_id) if not account: return make_response( jsonify({'error': 'Account %d not found' % account_id}), 404) if not request.json: return make_response( jsonify({'error': 'No data on account update'}), 400) if ('nickname' not in request.json or not isinstance(request.json['nickname'], str)): return make_response( jsonify({'error': 'Bad account nickname (%s)' % nickname}), 400) account.nickname = request.json['nickname'] try: db.session().commit() except exc.IntegrityError as ex: return make_response(jsonify({'error': 'update error'}), 409) except exc.SQLAlchemyError as ex: abort(500, ex) return jsonify(account)
def new_label(self, name, gid): """Create a new label in the database. Translate Gmail category labels to something that is easier to read. """ session = db.session() label = Label.as_unique(session, name=name, gid=gid, account=self.account) session.commit() self.label_map[label.gid] = label self.label_imap[label.id] = label.gid
def post(self): if not request.json: return make_response(jsonify({'error': 'No data'}), 400) if ('nickname' not in request.json or not isinstance(request.json['nickname'], str)): return make_response(jsonify({'error': 'Bad account nickname'}), 400) if ('email' not in request.json or not isinstance(request.json['email'], str)): return make_response(jsonify({'error': 'Bad account email'}), 400) account = Account(email=request.json['email'], nickname=request.json['nickname'], writable=request.json.get('writable', False), can_send=request.json.get('can-send', False)) try: db.session().add(account) db.session().commit() except exc.IntegrityError as ex: return make_response(jsonify({'error': 'Acount already exists'}), 409) except exc.SQLAlchemyError as ex: abort(500, ex) return jsonify(account)
def __init__(self, **kwargs): """Initialize a new object using the options passed in.""" def flatten(d, parent_key='', sep='_'): """Flatten hierarchical options by adding '_'.""" items = [] for k, v in d.items(): new_key = parent_key + sep + k if parent_key else k if isinstance(v, collections.MutableMapping): items.extend(flatten(v, new_key, sep=sep).items()) else: items.append((new_key, v)) return dict(items) self.options = self.options.push(flatten(kwargs)) self.set(account=Account.as_unique(db.session(), email=self.options.email, nickname=self.options.nickname, writable=self.options.writable, can_send=False)) db.session.flush() self.label_map = {} self.label_imap = {}
def test_incremental_pull2(client, sync): msg = {'from_id': 1, 'account_id': 1, 'article_id': 1, 'google_id': '163e42f92b6526f8', 'message_id': '<*****@*****.**>', 'thread_id': 1, 'from_id': 222, 'date': datetime.datetime(2018, 6, 9, 11, 55, 11), 'subject': 'This is a test message', 'references': '', 'snippet': 'This is the body of the message', 'size': 6271} sess = db.session() mm = Message(**msg) sess.add(mm) sess.commit() # Move history back a few steps so we get some updates. sync.sql3.set_history_id(290982) sync.pull() print("history is %d" % sync.sql3.get_history_id()) assert sync.sql3.get_history_id() == 291126
def create(self, msgs): """Create a new message in the database. This assumes we already created a placeholder row for the message and focus instead on extracting the required information from the representation we get from Gmail. This function will "create" multiple messages at once for efficiency--it is called once per batch received from Gmail. """ if '__getitem__' not in dir(msgs): msgs = (msgs, ) msgs = sorted(msgs, key=lambda msg: int(msg['id'], 16)) gids = [m['id'] for m in msgs] session = db.session() with session.no_autoflush: objs = session.query(Message).\ filter(and_(Message.google_id.in_(gids), Message.account == self.account)).\ order_by(Message.google_id.asc()).all() if len(objs) != len(msgs): pdb.set_trace() pass assert len(objs) == len(msgs) for obj, msg in zip(objs, msgs): assert obj.google_id == msg['id'] values = self.process_gmail_message(session, obj.id, msg) for key, value in values.items(): setattr(obj, key, value) try: session.commit() except exc.SQLAlchemyError as ex: pdb.set_trace() pass
def commit(self): """Commit pending session changes to the database.""" db.session().commit()
def put(self, account_id, article_id): def process_message(message, added, removed): for label in added: if label in message.labels: return message.id message.labels.append(label) for label in removed: if label not in message.labels: return message.id message.labels.remove(label) return message account = Account.query.get_or_404(account_id) if not account.writable: abort(403, 'Account is not writable') if not request.json: return make_response( jsonify({'error': 'No data for message update'}), 400) if ('add_labels' not in request.json and 'rm_labels' not in request.json): return make_response( jsonify({'error': 'Specify add_labels or rm_labels'}), 400) added = [ account.labels.filter_by(name=name).first_or_404() for name in request.json.get('add_labels', []) or [] ] removed = [ account.labels.filter_by(name=name).first_or_404() for name in request.json.get('rm_labels', []) or [] ] sync = get_sync(account) query = Message.query.filter_by(account_id=account_id) if account_id and not article_id: if 'article-id' not in request.json: abort(404) ids = get_article_ids(request.json['article-id']) count = len(ids) query = query.filter(Message.article_id.in_(ids)) else: count = 1 query = query.filter(Message.article_id == article_id) messages = query.all() if len(messages) != count: return make_response(jsonify({'error': 'Message not found'}), 404) resp = sync.remote_batch_update([m.google_id for m in messages], [l.gid for l in added], [l.gid for l in removed]) if resp: click.echo('remote update failed') abort(resp[0], resp[1]), session = db.session() resp = [process_message(m, added, removed) for m in messages] try: session.commit() except exc.IntegrityError as ex: return make_response(jsonify({'error': 'update error'}), 409) except exc.SQLAlchemyError as ex: abort(500, ex) failures = tuple(filter(lambda r: isinstance(r, int), resp)) return jsonify({'failures': failures})