Exemplo n.º 1
0
def test_delete_request_removes_data(conf, env, fxa_account, fxa_urls,
                                     fxa_client):
    if env == 'prod':
        pytest.skip('kintowe GDPR tests are not run in production')

    auth = FxABearerTokenAuth(
        fxa_account.email,
        fxa_account.password,
        scopes=['sync:addon_storage'],
        client_id=DEFAULT_CLIENT_ID,
        account_server_url=fxa_urls['authentication'],
        oauth_server_url=fxa_urls['oauth'],
    )

    # Add some data to chrome.storage (kintowe)
    we_client = Client(server_url=conf.get(env, 'we_server_url'), auth=auth)
    we_existing_records = we_client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                                bucket='default')
    assert len(we_existing_records) == 0
    data = {"payload": {"encrypted": "SmluZ28gdGVzdA=="}}
    we_record = we_client.create_record(
        data=data,
        collection=conf.get(env, 'qa_collection'),
        bucket='default',
        permissions={"read": ["system.Everyone"]})
    we_record_id = we_record['data']['id']
    we_updated_records = we_client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                               bucket='default')
    assert len(we_updated_records) == len(we_existing_records) + 1

    # Get the aliases of the bucket we are putting data in and
    # make sure that an unauthenticated user can see these records
    # before we delete the account
    we_bucket_id = we_client.server_info()["user"]["bucket"]
    anon_we_client = Client(server_url=conf.get(env, 'we_server_url'))
    resp = anon_we_client.get_record(id=we_record_id,
                                     bucket=we_bucket_id,
                                     collection=conf.get(env, 'qa_collection'))
    assert resp['data']['id'] == we_record_id

    # Delete FxA account
    fxa_client.destroy_account(fxa_account.email, fxa_account.password)

    # Wait 1 minute and then make sure the records do not exist because the
    # Kinto client will throw an exception for non-existent records
    time.sleep(60)
    with pytest.raises(KintoException):
        resp = anon_we_client.get_record(id=we_record_id,
                                         bucket=we_bucket_id,
                                         collection=conf.get(
                                             env, 'qa_collection'))
Exemplo n.º 2
0
def test_delete_request_removes_data(conf, env, fxa_account, fxa_urls,
                                     fxa_client):
    if env == 'prod':
        pytest.skip('testpilot GDPR tests are not run in production')

    auth = FxABearerTokenAuth(
        fxa_account.email,
        fxa_account.password,
        client_id=DEFAULT_CLIENT_ID,
        scopes=["https://identity.mozilla.com/apps/notes"],
        account_server_url=fxa_urls['authentication'],
        oauth_server_url=fxa_urls['oauth'],
    )

    # Add some data to the Notes collection
    tp_client = Client(server_url=conf.get(env, 'tp_server_url'), auth=auth)
    tp_existing_records = tp_client.get_records(collection='notes',
                                                bucket='default')
    assert len(tp_existing_records) == 0
    data = {"subject": "QA Test", "value": "This stuff should get deleted"}
    tp_record = tp_client.create_record(
        data=data,
        collection='notes',
        permissions={"read": ["system.Everyone"]})
    tp_record_id = tp_record['data']['id']
    tp_updated_records = tp_client.get_records(collection='notes',
                                               bucket='default')
    assert len(tp_updated_records) == len(tp_existing_records) + 1

    # Get the aliases of the bucket we are putting data in and
    # make sure that an unauthenticated user can see these records
    # before we delete the account
    tp_bucket_id = tp_client.server_info()["user"]["bucket"]
    anon_tp_client = Client(server_url=conf.get(env, 'tp_server_url'))
    resp = anon_tp_client.get_record(id=tp_record_id,
                                     bucket=tp_bucket_id,
                                     collection='notes')
    assert resp['data']['id'] == tp_record_id

    # Delete FxA account
    fxa_client.destroy_account(fxa_account.email, fxa_account.password)

    # Wait 5 minutes and then make sure the records do not exist because the
    # Kinto client will throw an exception for non-existent records
    time.sleep(120)

    with pytest.raises(KintoException):
        resp = anon_tp_client.get_record(id=tp_record_id,
                                         bucket=tp_bucket_id,
                                         collection='notes')
Exemplo n.º 3
0
def test_add_content(env, conf):
    # Grab a bearer token that we can use to talk to the webextensions endpoint
    acct = TestEmailAccount()
    email = acct.email
    passwd = str(uuid.uuid4())
    fxaclient = FxaClient("https://api.accounts.firefox.com")
    session = fxaclient.create_account(email, passwd)
    m = acct.wait_for_email(lambda m: "x-verify-code" in m["headers"])

    if m is None:
        raise RuntimeErrors("Verification email did not arrive")

    session.verify_email_code(m["headers"]["x-verify-code"])
    auth = FxABearerTokenAuth(
        email,
        passwd,
        scopes=['sync:addon_storage'],
        client_id=DEFAULT_CLIENT_ID,
        account_server_url=conf.get(env, 'account_server_url'),
        oauth_server_url=conf.get(env, 'oauth_server_url'),
    )
    client = Client(server_url=conf.get(env, 'we_server_url'), auth=auth)

    # Add a record to our QA collection and make sure we have N+1 records
    existing_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                          bucket='default')
    assert len(existing_records) == 0

    data = {"payload": {"encrypted": "SmluZ28gdGVzdA=="}}
    resp = client.create_record(data=data,
                                collection=conf.get(env, 'qa_collection'),
                                bucket='default')
    new_record_id = resp['data']['id']
    updated_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                         bucket='default')
    assert len(updated_records) == len(existing_records) + 1

    client.delete_record(id=new_record_id,
                         collection=conf.get(env, 'qa_collection'))
    updated_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                         bucket='default')
    assert len(updated_records) == len(existing_records)

    # Clean up the account that we created for the test
    acct.clear()
    fxaclient.destroy_account(email, passwd)
Exemplo n.º 4
0
def validate_changes_collection(event, context, **kwargs):
    """Validate the entries of the monitor endpoint.
    """
    # 1. Grab the changes collection
    server_url = event["server"]
    bucket = event.get("bucket", os.getenv("BUCKET", "monitor"))
    collection = event.get("collection", os.getenv("COLLECTION", "changes"))

    client = Client(server_url=server_url,
                    bucket=bucket,
                    collection=collection)
    print("Looking at %s: " % client.get_endpoint("collection"))

    collections = client.get_records()
    # 2. For each collection there, validate the ETag
    everything_ok = True
    for collection in collections:
        bid = collection["bucket"]
        cid = collection["collection"]
        last_modified = collection["last_modified"]
        etag = client.get_records_timestamp(bucket=bid, collection=cid)
        if str(etag) == str(last_modified):
            print("Etag OK for {}/{} : {}".format(bid, cid, etag))
        else:
            everything_ok = False
            print("Etag NOT OK for {}/{} : {} != {}".format(
                bid, cid, last_modified, etag))

    if not everything_ok:
        raise ValueError("One of the collection did not validate.")
Exemplo n.º 5
0
    def _has_inconsistencies(server_url, auth, r):
        client = Client(server_url=server_url, auth=auth)

        source_metadata = client.get_collection(
            bucket=r["source"]["bucket"], id=r["source"]["collection"])["data"]
        status = source_metadata["status"]

        identifier = "{bucket}/{collection}".format(**r["destination"])

        # Collection status is reset on any modification, so if status is ``to-review``,
        # then records in the source should be exactly the same as the records in the preview
        if status == "to-review":
            source_records = client.get_records(**r["source"])
            preview_records = client.get_records(**r["preview"])
            diff = compare_collections(source_records, preview_records)
            if diff:
                return identifier, diff

        # And if status is ``signed``, then records in the source and preview should
        # all be the same as those in the destination.
        elif status == "signed" or status is None:
            source_records = client.get_records(**r["source"])
            dest_records = client.get_records(**r["destination"])
            if "preview" in r:
                # If preview is enabled, then compare source/preview and preview/dest
                preview_records = client.get_records(**r["preview"])
                diff_source = compare_collections(source_records,
                                                  preview_records)
                diff_preview = compare_collections(preview_records,
                                                   dest_records)
            else:
                # Otherwise, just compare source/dest
                diff_source = compare_collections(source_records, dest_records)
                diff_preview = []
            # If difference detected, report it!
            if diff_source or diff_preview:
                return identifier, diff_source + diff_preview

        else:
            # And if status is ``work-in-progress``, we can't really check anything.
            # Source can differ from preview, and preview can differ from destination
            # if a review request was previously rejected.
            print(f"{identifier} SKIP ({status})")
            return identifier, None

        print(f"{identifier} OK")
        return identifier, None
Exemplo n.º 6
0
 def test_multiple_record_deletion(self):
     client = Client(server_url=self.server_url, auth=self.auth,
                     bucket='mozilla', collection='payments')
     client.create_bucket()
     client.create_collection()
     client.create_record({'foo': 'bar'})
     client.delete_records()
     assert len(client.get_records()) == 0
Exemplo n.º 7
0
 def test_multiple_record_deletion(self):
     client = Client(server_url=self.server_url, auth=self.auth,
                     bucket='mozilla', collection='payments')
     client.create_bucket()
     client.create_collection()
     client.create_record(data={'foo': 'bar'})
     client.delete_records()
     assert len(client.get_records()) == 0
Exemplo n.º 8
0
 def test_records_list_retrieval(self):
     client = Client(server_url=self.server_url, auth=self.auth,
                     bucket='mozilla', collection='payments')
     client.create_bucket()
     client.create_collection()
     client.create_record(data={'foo': 'bar'},
                          permissions={'read': ['alexis']})
     records = client.get_records()
     assert len(records) == 1
Exemplo n.º 9
0
 def test_records_list_retrieval(self):
     client = Client(server_url=self.server_url, auth=self.auth,
                     bucket='mozilla', collection='payments')
     client.create_bucket()
     client.create_collection()
     client.create_record(data={'foo': 'bar'},
                          permissions={'read': ['account:alexis']})
     records = client.get_records()
     assert len(records) == 1
Exemplo n.º 10
0
 def test_one_record_deletion(self):
     client = Client(server_url=self.server_url, auth=self.auth,
                     bucket='mozilla', collection='payments')
     client.create_bucket()
     client.create_collection()
     record = client.create_record(data={'foo': 'bar'})
     deleted = client.delete_record(id=record['data']['id'])
     assert deleted['deleted'] is True
     assert len(client.get_records()) == 0
Exemplo n.º 11
0
 def test_one_record_deletion(self):
     client = Client(server_url=self.server_url, auth=self.auth,
                     bucket='mozilla', collection='payments')
     client.create_bucket()
     client.create_collection()
     record = client.create_record({'foo': 'bar'})
     deleted = client.delete_record(record['data']['id'])
     assert deleted['deleted'] is True
     assert len(client.get_records()) == 0
Exemplo n.º 12
0
 def test_multiple_record_deletion(self):
     client = Client(server_url=self.server_url,
                     auth=self.auth,
                     bucket="mozilla",
                     collection="payments")
     client.create_bucket()
     client.create_collection()
     client.create_record(data={"foo": "bar"})
     client.delete_records()
     assert len(client.get_records()) == 0
Exemplo n.º 13
0
 def test_records_paginated_list_retrieval(self):
     client = Client(server_url=self.server_url, auth=self.auth,
                     bucket='mozilla', collection='payments')
     client.create_bucket()
     client.create_collection()
     for i in range(10):
         client.create_record(data={'foo': 'bar'},
                              permissions={'read': ['account:alexis']})
     # Kinto is running with kinto.paginate_by = 5
     records = client.get_records()
     assert len(records) == 10
Exemplo n.º 14
0
def fetch_signed_resources(server_url, auth):
    # List signed collection using capabilities.
    client = Client(server_url=server_url,
                    auth=auth,
                    bucket="monitor",
                    collection="changes")
    info = client.server_info()
    try:
        resources = info["capabilities"]["signer"]["resources"]
    except KeyError:
        raise ValueError(
            "No signer capabilities found. Run on *writer* server!")

    # Build the list of signed collections, source -> preview -> destination
    # For most cases, configuration of signed resources is specified by bucket and
    # does not contain any collection information.
    resources_by_bid = {}
    resources_by_cid = {}
    preview_buckets = set()
    for resource in resources:
        if resource["source"]["collection"] is not None:
            resources_by_cid[(
                resource["destination"]["bucket"],
                resource["destination"]["collection"],
            )] = resource
        else:
            resources_by_bid[resource["destination"]["bucket"]] = resource
        if "preview" in resource:
            preview_buckets.add(resource["preview"]["bucket"])

    print("Read collection list from {}".format(
        client.get_endpoint("collection")))
    resources = []
    monitored = client.get_records(_sort="bucket,collection")
    for entry in monitored:
        bid = entry["bucket"]
        cid = entry["collection"]

        # Skip preview collections entries
        if bid in preview_buckets:
            continue

        if (bid, cid) in resources_by_cid:
            r = resources_by_cid[(bid, cid)]
        elif bid in resources_by_bid:
            r = copy.deepcopy(resources_by_bid[bid])
            r["source"]["collection"] = r["destination"]["collection"] = cid
            if "preview" in r:
                r["preview"]["collection"] = cid
        else:
            raise ValueError(f"Unknown signed collection {bid}/{cid}")
        resources.append(r)

    return resources
Exemplo n.º 15
0
 def test_one_record_deletion(self):
     client = Client(server_url=self.server_url,
                     auth=self.auth,
                     bucket="mozilla",
                     collection="payments")
     client.create_bucket()
     client.create_collection()
     record = client.create_record(data={"foo": "bar"})
     deleted = client.delete_record(id=record["data"]["id"])
     assert deleted["deleted"] is True
     assert len(client.get_records()) == 0
Exemplo n.º 16
0
 def test_records_list_retrieval(self):
     client = Client(server_url=self.server_url,
                     auth=self.auth,
                     bucket="mozilla",
                     collection="payments")
     client.create_bucket()
     client.create_collection()
     client.create_record(data={"foo": "bar"},
                          permissions={"read": ["account:alexis"]})
     records = client.get_records()
     assert len(records) == 1
Exemplo n.º 17
0
 def test_records_paginated_list_retrieval(self):
     client = Client(server_url=self.server_url, auth=self.auth,
                     bucket='mozilla', collection='payments')
     client.create_bucket()
     client.create_collection()
     for i in range(10):
         client.create_record(data={'foo': 'bar'},
                              permissions={'read': ['alexis']})
     # Kinto is running with kinto.paginate_by = 5
     records = client.get_records()
     assert len(records) == 10
Exemplo n.º 18
0
 def test_records_paginated_list_retrieval(self):
     client = Client(server_url=self.server_url,
                     auth=self.auth,
                     bucket="mozilla",
                     collection="payments")
     client.create_bucket()
     client.create_collection()
     for i in range(10):
         client.create_record(data={"foo": "bar"},
                              permissions={"read": ["account:alexis"]})
     # Kinto is running with kinto.paginate_by = 5
     records = client.get_records()
     assert len(records) == 10
def download_collection_data(server_url, collection):
    client = Client(
        server_url=server_url,
        bucket=collection["bucket"],
        collection=collection["collection"],
    )
    endpoint = client.get_endpoint("collection")
    # Collection metadata with cache busting
    metadata = client.get_collection(
        _expected=collection["last_modified"])["data"]
    # Download records with cache busting
    records = client.get_records(_sort="-last_modified",
                                 _expected=collection["last_modified"])
    timestamp = client.get_records_timestamp()
    return (collection, endpoint, metadata, records, timestamp)
def test_add_content(env, conf, fxa_account, fxa_urls):
    if env == 'prod':
        pytest.skip('qa cannot create records in production')

    auth = FxABearerTokenAuth(
        fxa_account.email,
        fxa_account.password,
        scopes=['sync:addon_storage'],
        client_id=DEFAULT_CLIENT_ID,
        account_server_url=fxa_urls['authentication'],
        oauth_server_url=fxa_urls['oauth'],
    )
    client = Client(server_url=conf.get(env, 'we_server_url'), auth=auth)

    # Add a record to our QA collection and make sure we have N+1 records
    existing_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                          bucket='default')
    assert len(existing_records) == 0

    data = {"payload": {"encrypted": "SmluZ28gdGVzdA=="}}
    resp = client.create_record(data=data,
                                collection=conf.get(env, 'qa_collection'),
                                bucket='default')
    new_record_id = resp['data']['id']
    updated_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                         bucket='default')
    assert len(updated_records) == len(existing_records) + 1

    client.delete_record(id=new_record_id,
                         collection=conf.get(env, 'qa_collection'))
    updated_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                         bucket='default')
    assert len(updated_records) == len(existing_records)
Exemplo n.º 21
0
def get_nightly_builds(from_datestr="2018-10", to_datestr="2018-11"):
    """Return nightly build data for all builds."""
    client = KintoClient(server_url=BUILDHUB_URL)
    records = client.get_records(
        **{
            "target.platform": "linux-x86_64",
            "target.channel": "nightly",
            "source.product": "firefox",
            "target.locale": "en-US",
            # Caution: use build.date because download.date is crazy for dates before
            # 2016.
            "gt_build.date": from_datestr,
            "lt_build.date": to_datestr,
            "_sort": "-download.date",
            "_fields": "build.id,source.revision,download.date",
            # "_limit": 10,
        },
        bucket="build-hub",
        collection="releases",
    )
    return records
def test_blocklist_timestamp(env, conf):
    if env == 'prod':
        pytest.skip('Skipping blocklist timestamp test in production')

    client = Client(server_url=conf.get(env, 'reader_server'),
                    bucket='blocklists')
    # Take the highest timestamp of the collections contained in the blocklist.xml.
    last_modified = -1
    for cid in ('addons', 'plugins', 'gfx'):
        records = client.get_records(collection=cid,
                                     _sort='-last_modified',
                                     _limit=1,
                                     enabled='true')
        if len(records) > 0:
            last_modified = max(last_modified, records[0]['last_modified'])

    # Read the current XML blocklist ETag.
    blocklist_uri = conf.get(env, 'reader_server').strip('/') + (
        '/blocklist/3/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}/58.0'
        '/Firefox/20180123185941/Darwin_x86_64-gcc3-u-i386-x86_64'
        '/en-US/release/Darwin 17.4.0/default/default/invalid/invalid/0/')
    r = requests.get(blocklist_uri)
    r.raise_for_status()
    etag_header = int(r.headers.get('ETag', '')[1:-1])
    last_modified_header = r.headers.get('Last-Modified', '')
    last_modified_header = datetime.datetime.strptime(
        last_modified_header, '%a, %d %b %Y %H:%M:%S GMT')

    # Check XML attribute <blocklist lastupdate="1483471392954">
    dom = minidom.parseString(r.text)
    root = dom.getElementsByTagName('blocklist')[0]
    last_modified_attr = int(root.getAttribute('lastupdate'))

    # Make sure they all match.
    # See https://bugzilla.mozilla.org/show_bug.cgi?id=1436469
    assert last_modified == etag_header
    last_modified_dt = datetime.datetime.utcfromtimestamp(last_modified /
                                                          1000.0)
    assert last_modified_dt.replace(microsecond=0) == last_modified_header
    assert last_modified == last_modified_attr
Exemplo n.º 23
0
def getmap(collection_id):
    processed_projects_list = []

    for file in os.listdir("projects"):
        if file.endswith(".json"):
            file = os.path.join("projects", file)
            with open(file) as json_data:
                d = json.load(json_data)
                processed_projects_list.append(d['id'])

    auth = (username, password)
    client = Client(server_url=server_url, auth=auth)
    try:
        records = client.get_records(bucket='formdata',
                                     collection=collection_id)
    except:
        return 'There was a problem getting the information from kinto'

    for record in records:
        if record['id'] in processed_projects_list:
            continue
        else:
            label = record['label']
            print(f'Processing JSON file for: {label}')
            record['element type'] = "Project"
            tagline = record['tag line']
            description1 = record['Description1']
            description2 = record['Description2']
            video = record['video src']
            markdown = f'#{tagline}\n##About {label}\n![About {label}]({video})\n ###{description1}\n{description2}'
            record['description'] = markdown

            stack_list = []
            if 'Stack' in record:
                if isinstance(record['Stack'], (list, )):
                    for value in record['Stack']:
                        stack_list.append(remove_parens(value))
                else:
                    stack_list.append(remove_parens(record['Stack']))

                record['Stack'] = stack_list

            network_list = []
            if 'Network Topology' in record:
                if isinstance(record['Network Topology'], (list, )):
                    for value in record['Network Topology']:
                        network_list.append(remove_parens(value))
                else:
                    network_list.append(
                        remove_parens(record['Network Topology']))
                record['Network Topology'] = network_list

            # these are fileds that have comma sep strings entered by the users
            string_fields = [
                'Tags', 'Suggested Groups', 'Relies On',
                'Suggested Areas of Work', 'Regional Traction',
                'Suggested Values', 'Additions'
            ]

            for key in string_fields:
                record[key] = string_to_list(data_dict=record,
                                             key=key,
                                             delimiter=',')

            # remove contact and email for privacy
            del record['contact email']
            del record['map contact']

            # write out the files
            with open(f'projects/{label}.json', 'w') as outfile:
                json.dump(record, outfile)

    make_map_json()
    return "All files processed"
Exemplo n.º 24
0
class FunctionalTest(unittest.TestCase):
    def setUp(self):
        super().setUp()
        # XXX Read the configuration from env variables.
        self.server_url = SERVER_URL
        self.auth = DEFAULT_AUTH

        # Read the configuration.
        self.config = configparser.RawConfigParser()
        self.config.read(os.path.join(__HERE__, "config/kinto.ini"))
        self.client = Client(server_url=self.server_url, auth=self.auth)
        self.create_user(self.auth)

    def tearDown(self):
        # Delete all the created objects
        flush_url = urljoin(self.server_url, "/__flush__")
        resp = requests.post(flush_url)
        resp.raise_for_status()

    def create_user(self, credentials):
        account_url = urljoin(self.server_url,
                              "/accounts/{}".format(credentials[0]))
        r = requests.put(account_url,
                         json={"data": {
                             "password": credentials[1]
                         }},
                         auth=DEFAULT_AUTH)
        r.raise_for_status()
        return r.json()

    def get_user_id(self, credentials):
        r = self.create_user(credentials)
        return "account:{}".format(r["data"]["id"])

    def test_bucket_creation(self):
        bucket = self.client.create_bucket(id="mozilla")
        user_id = self.get_user_id(self.auth)
        assert user_id in bucket["permissions"]["write"]

    def test_bucket_creation_if_not_exists(self):
        self.client.create_bucket(id="mozilla")
        # Should not raise.
        self.client.create_bucket(id="mozilla", if_not_exists=True)

    def test_buckets_retrieval(self):
        self.client.create_bucket(id="mozilla")
        buckets = self.client.get_buckets()
        assert len(buckets) == 1

    def test_bucket_retrieval(self):
        self.client.create_bucket(id="mozilla")
        self.client.get_bucket(id="mozilla")
        # XXX Add permissions handling during creation and check they are
        # present during retrieval.

    def test_bucket_modification(self):
        bucket = self.client.create_bucket(id="mozilla", data={"version": 1})
        assert bucket["data"]["version"] == 1
        bucket = self.client.patch_bucket(id="mozilla", data={"author": "you"})
        assert bucket["data"]["version"] == 1
        assert bucket["data"]["author"] == "you"
        bucket = self.client.update_bucket(id="mozilla",
                                           data={"date": "today"})
        assert bucket["data"]["date"] == "today"
        assert "version" not in bucket["data"]

    def test_bucket_retrieval_fails_when_not_created(self):
        self.assertRaises(BucketNotFound,
                          self.client.get_bucket,
                          id="non-existent")

    def test_bucket_deletion(self):
        self.client.create_bucket(id="mozilla")
        self.client.delete_bucket(id="mozilla")
        self.assertRaises(BucketNotFound, self.client.get_bucket, id="mozilla")

    def test_bucket_deletion_if_exists(self):
        self.client.create_bucket(id="mozilla")
        self.client.delete_bucket(id="mozilla")
        self.client.delete_bucket(id="mozilla", if_exists=True)

    def test_buckets_deletion(self):
        self.client.create_bucket(id="mozilla")
        buckets = self.client.delete_buckets()
        assert buckets[0]["id"] == "mozilla"
        self.assertRaises(BucketNotFound, self.client.get_bucket, id="mozilla")

    def test_buckets_deletion_when_no_buckets_exist(self):
        deleted_buckets = self.client.delete_buckets()
        assert len(deleted_buckets) == 0

    def test_bucket_save(self):
        self.client.create_bucket(id="mozilla",
                                  permissions={"write": ["account:alexis"]})
        bucket = self.client.get_bucket(id="mozilla")
        assert "account:alexis" in bucket["permissions"]["write"]

    def test_group_creation(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(
            id="payments",
            bucket="mozilla",
            data={"members": ["blah"]},
            permissions={"write": ["blah"]},
        )
        # Test retrieval of a group gets the permissions as well.
        group = self.client.get_group(id="payments", bucket="mozilla")
        assert "blah" in group["permissions"]["write"]

    def test_group_creation_if_not_exists(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(id="payments",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.create_group(
            id="payments",
            bucket="mozilla",
            data={"members": ["blah"]},
            permissions={"write": ["blah"]},
            if_not_exists=True,
        )

    def test_group_creation_if_bucket_does_not_exist(self):
        with pytest.raises(KintoException) as e:
            self.client.create_group(id="payments",
                                     bucket="mozilla",
                                     data={"members": ["blah"]})
        assert str(e).endswith(
            "PUT /v1/buckets/mozilla/groups/payments - "
            "403 Unauthorized. Please check that the "
            "bucket exists and that you have the permission "
            "to create or write on this group.")

    def test_group_update(self):
        self.client.create_bucket(id="mozilla")
        group = self.client.create_group(id="payments",
                                         bucket="mozilla",
                                         data={"members": ["blah"]},
                                         if_not_exists=True)
        assert group["data"]["members"][0] == "blah"
        group = self.client.update_group(data={"members": ["blah", "foo"]},
                                         id="payments",
                                         bucket="mozilla")
        self.assertEqual(group["data"]["members"][1], "foo")

    def test_group_list(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(id="receipts",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.create_group(id="assets",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        # The returned groups should be strings.
        groups = self.client.get_groups(bucket="mozilla")
        self.assertEqual(2, len(groups))
        self.assertEqual(set([coll["id"] for coll in groups]),
                         set(["receipts", "assets"]))

    def test_group_deletion(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(id="payments",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.delete_group(id="payments", bucket="mozilla")
        assert len(self.client.get_groups(bucket="mozilla")) == 0

    def test_group_deletion_if_exists(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(id="payments",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.delete_group(id="payments", bucket="mozilla")
        self.client.delete_group(id="payments",
                                 bucket="mozilla",
                                 if_exists=True)

    def test_group_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session,
                               "request",
                               side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_group(id="payments",
                                         bucket="mozilla",
                                         if_exists=True)

    def test_groups_deletion(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(id="amo",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.create_group(id="blocklist",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.delete_groups(bucket="mozilla")
        assert len(self.client.get_groups(bucket="mozilla")) == 0

    def test_groups_deletion_when_no_groups_exist(self):
        self.client.create_bucket(id="mozilla")
        deleted_groups = self.client.delete_groups(bucket="mozilla")
        assert len(deleted_groups) == 0

    def test_collection_creation(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(
            id="payments",
            bucket="mozilla",
            permissions={"write": ["account:alexis"]})

        # Test retrieval of a collection gets the permissions as well.
        collection = self.client.get_collection(id="payments",
                                                bucket="mozilla")
        assert "account:alexis" in collection["permissions"]["write"]

    def test_collection_not_found(self):
        self.client.create_bucket(id="mozilla")

        with pytest.raises(CollectionNotFound):
            self.client.get_collection(id="payments", bucket="mozilla")

    def test_collection_access_forbidden(self):
        with pytest.raises(KintoException):
            self.client.get_collection(id="payments", bucket="mozilla")

    def test_collection_creation_if_not_exists(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(id="payments", bucket="mozilla")
        # Should not raise.
        self.client.create_collection(id="payments",
                                      bucket="mozilla",
                                      if_not_exists=True)

    def test_collection_list(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(id="receipts", bucket="mozilla")
        self.client.create_collection(id="assets", bucket="mozilla")

        # The returned collections should be strings.
        collections = self.client.get_collections(bucket="mozilla")
        self.assertEqual(len(collections), 2)

        self.assertEqual(set([coll["id"] for coll in collections]),
                         set(["receipts", "assets"]))

    def test_collection_deletion(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(id="payments", bucket="mozilla")
        self.client.delete_collection(id="payments", bucket="mozilla")
        assert len(self.client.get_collections(bucket="mozilla")) == 0

    def test_collection_deletion_if_exists(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(id="payments", bucket="mozilla")
        self.client.delete_collection(id="payments", bucket="mozilla")
        self.client.delete_collection(id="payments",
                                      bucket="mozilla",
                                      if_exists=True)

    def test_collection_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session,
                               "request",
                               side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_collection(id="payments",
                                              bucket="mozilla",
                                              if_exists=True)

    def test_collections_deletion(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(id="amo", bucket="mozilla")
        self.client.create_collection(id="blocklist", bucket="mozilla")
        self.client.delete_collections(bucket="mozilla")
        assert len(self.client.get_collections(bucket="mozilla")) == 0

    def test_collections_deletion_when_no_collections_exist(self):
        self.client.create_bucket(id="mozilla")
        deleted_collections = self.client.delete_collections(bucket="mozilla")
        assert len(deleted_collections) == 0

    def test_record_creation_and_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        created = client.create_record(
            data={"foo": "bar"}, permissions={"read": ["account:alexis"]})
        record = client.get_record(id=created["data"]["id"])
        assert "account:alexis" in record["permissions"]["read"]

    def test_records_list_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        client.create_record(data={"foo": "bar"},
                             permissions={"read": ["account:alexis"]})
        records = client.get_records()
        assert len(records) == 1

    def test_records_timestamp_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={"foo": "bar"},
                                      permissions={"read": ["account:alexis"]})
        etag = client.get_records_timestamp()
        assert str(etag) == str(record["data"]["last_modified"])

    def test_records_paginated_list_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        for i in range(10):
            client.create_record(data={"foo": "bar"},
                                 permissions={"read": ["account:alexis"]})
        # Kinto is running with kinto.paginate_by = 5
        records = client.get_records()
        assert len(records) == 10

    def test_records_generator_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        for i in range(10):
            client.create_record(data={"foo": "bar"},
                                 permissions={"read": ["account:alexis"]})

        pages = list(client.get_paginated_records())

        assert len(pages) == 2

    def test_single_record_save(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        created = client.create_record(
            data={"foo": "bar"}, permissions={"read": ["account:alexis"]})
        created["data"]["bar"] = "baz"

        # XXX enhance this in order to have to pass only one argument, created.
        client.update_record(id=created["data"]["id"], data=created["data"])

        retrieved = client.get_record(id=created["data"]["id"])
        assert "account:alexis" in retrieved["permissions"]["read"]
        assert retrieved["data"]["foo"] == u"bar"
        assert retrieved["data"]["bar"] == u"baz"
        assert created["data"]["id"] == retrieved["data"]["id"]

    def test_single_record_doesnt_overwrite(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        created = client.create_record(
            data={"foo": "bar"}, permissions={"read": ["account:alexis"]})

        with self.assertRaises(KintoException):
            # Create a second record with the ID of the first one.
            client.create_record(data={
                "id": created["data"]["id"],
                "bar": "baz"
            })

    def test_single_record_creation_if_not_exists(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={"foo": "bar"})
        client.create_record(data={
            "id": created["data"]["id"],
            "bar": "baz"
        },
                             if_not_exists=True)

    def test_single_record_can_overwrite(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        created = client.create_record(
            data={"foo": "bar"}, permissions={"read": ["account:alexis"]})

        client.create_record(data={
            "id": created["data"]["id"],
            "bar": "baz"
        },
                             safe=False)

    def test_one_record_deletion(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={"foo": "bar"})
        deleted = client.delete_record(id=record["data"]["id"])
        assert deleted["deleted"] is True
        assert len(client.get_records()) == 0

    def test_record_deletion_if_exists(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={"foo": "bar"})
        deleted = client.delete_record(id=record["data"]["id"])
        deleted_if_exists = client.delete_record(id=record["data"]["id"],
                                                 if_exists=True)
        assert deleted["deleted"] is True
        assert deleted_if_exists is None

    def test_multiple_record_deletion(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        client.create_record(data={"foo": "bar"})
        client.delete_records()
        assert len(client.get_records()) == 0

    def test_records_deletion_when_no_records_exist(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        deleted_records = client.delete_records()
        assert len(deleted_records) == 0

    def test_bucket_sharing(self):
        alice_credentials = ("alice", "p4ssw0rd")
        alice_userid = self.get_user_id(alice_credentials)

        # Create a bucket and share it with alice.
        self.client.create_bucket(id="shared-bucket",
                                  permissions={"read": [alice_userid]})

        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_bucket(id="shared-bucket")

    def test_updating_data_on_a_group(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla")
        client.create_bucket()
        client.create_group(id="payments", data={"members": []})
        client.patch_group(id="payments", data={"secret": "psssssst!"})
        group = client.get_group(id="payments")
        assert group["data"]["secret"] == "psssssst!"

    def test_updating_data_on_a_collection(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()

        client.patch_collection(data={"secret": "psssssst!"})
        collection = client.get_collection()
        assert collection["data"]["secret"] == "psssssst!"

    def test_collection_sharing(self):
        alice_credentials = ("alice", "p4ssw0rd")
        alice_userid = self.get_user_id(alice_credentials)

        self.client.create_bucket(id="bob-bucket")
        self.client.create_collection(id="shared",
                                      bucket="bob-bucket",
                                      permissions={"read": [alice_userid]})

        # Try to read the collection as Alice.
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_collection(id="shared", bucket="bob-bucket")

    def test_record_sharing(self):
        alice_credentials = ("alice", "p4ssw0rd")
        alice_userid = self.get_user_id(alice_credentials)

        # Create a record, and share it with Alice.
        self.client.create_bucket(id="bob-bucket")
        self.client.create_collection(id="bob-personal-collection",
                                      bucket="bob-bucket")
        record = self.client.create_record(
            data={"foo": "bar"},
            permissions={"read": [alice_userid]},
            bucket="bob-bucket",
            collection="bob-personal-collection",
        )

        # Try to read the record as Alice
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        record = alice_client.get_record(id=record["data"]["id"],
                                         bucket="bob-bucket",
                                         collection="bob-personal-collection")

        assert record["data"]["foo"] == "bar"

    def test_request_batching(self):
        with self.client.batch(bucket="mozilla", collection="fonts") as batch:
            batch.create_bucket()
            batch.create_collection()
            batch.create_record(data={"foo": "bar"},
                                permissions={"read": ["natim"]})
            batch.create_record(data={"bar": "baz"},
                                permissions={"read": ["account:alexis"]})

        _, _, r1, r2 = batch.results()
        records = self.client.get_records(bucket="mozilla", collection="fonts")

        assert len(records) == 2
        assert records[0] == r2["data"]
        assert records[1] == r1["data"]

    def test_patch_record_jsonpatch(self):
        self.client.create_bucket(id="b1")
        self.client.create_collection(id="c1", bucket="b1")
        self.client.create_record(id="r1",
                                  collection="c1",
                                  bucket="b1",
                                  data={"hello": "world"})
        patch = JSONPatch([
            {
                "op": "add",
                "path": "/data/goodnight",
                "value": "moon"
            },
            {
                "op": "add",
                "path": "/permissions/read/alice"
            },
        ])
        self.client.patch_record(id="r1",
                                 collection="c1",
                                 bucket="b1",
                                 changes=patch)
        record = self.client.get_record(bucket="b1", collection="c1", id="r1")
        assert record["data"]["hello"] == "world"
        assert record["data"]["goodnight"] == "moon"
        assert record["permissions"]["read"] == ["alice"]

    def test_replication(self):
        # First, create a few records on the first kinto collection.
        with self.client.batch(bucket="origin", collection="coll") as batch:
            batch.create_bucket()
            batch.create_collection()

            for n in range(10):
                batch.create_record(data={"foo": "bar", "n": n})

        origin = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="origin",
                        collection="coll")
        destination = Client(server_url=self.server_url,
                             auth=self.auth,
                             bucket="destination",
                             collection="coll")

        replication.replicate(origin, destination)
        records = self.client.get_records(bucket="destination",
                                          collection="coll")
        assert len(records) == 10
Exemplo n.º 25
0
class FunctionalTest(unittest2.TestCase):
    def __init__(self, *args, **kwargs):
        super(FunctionalTest, self).__init__(*args, **kwargs)
        # XXX Read the configuration from env variables.
        self.server_url = SERVER_URL
        self.auth = DEFAULT_AUTH

        # Read the configuration.
        self.config = configparser.RawConfigParser()
        self.config.read(os.path.join(__HERE__, 'config/kinto.ini'))
        self.client = Client(server_url=self.server_url, auth=self.auth)

    def tearDown(self):
        # Delete all the created objects
        flush_url = urljoin(self.server_url, '/__flush__')
        resp = requests.post(flush_url)
        resp.raise_for_status()

    def get_user_id(self, credentials):
        hmac_secret = self.config.get('app:main', 'kinto.userid_hmac_secret')
        credentials = '%s:%s' % credentials
        digest = kinto_core_utils.hmac_digest(hmac_secret, credentials)
        return 'basicauth:%s' % digest

    def test_bucket_creation(self):
        bucket = self.client.create_bucket('mozilla')
        user_id = self.get_user_id(self.auth)
        assert user_id in bucket['permissions']['write']

    def test_bucket_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        # Should not raise.
        self.client.create_bucket('mozilla', if_not_exists=True)

    def test_buckets_retrieval(self):
        self.client.create_bucket('mozilla')
        buckets = self.client.get_buckets()
        assert len(buckets) == 1

    def test_bucket_retrieval(self):
        self.client.create_bucket('mozilla')
        self.client.get_bucket('mozilla')
        # XXX Add permissions handling during creation and check they are
        # present during retrieval.

    def test_bucket_modification(self):
        bucket = self.client.create_bucket('mozilla', data={'version': 1})
        assert bucket['data']['version'] == 1
        bucket = self.client.patch_bucket('mozilla', data={'author': 'you'})
        assert bucket['data']['version'] == 1
        assert bucket['data']['author'] == 'you'
        bucket = self.client.update_bucket('mozilla', data={'date': 'today'})
        assert bucket['data']['date'] == 'today'
        assert 'version' not in bucket['data']

    def test_bucket_retrieval_fails_when_not_created(self):
        self.assertRaises(BucketNotFound, self.client.get_bucket,
                          'non-existent')

    def test_bucket_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.delete_bucket('mozilla')
        self.assertRaises(BucketNotFound, self.client.get_bucket, 'mozilla')

    def test_bucket_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.delete_bucket('mozilla')
        self.client.delete_bucket('mozilla', if_exists=True)

    def test_buckets_deletion(self):
        self.client.create_bucket('mozilla')
        buckets = self.client.delete_buckets()
        assert buckets[0]['id'] == 'mozilla'
        self.assertRaises(BucketNotFound, self.client.get_bucket, 'mozilla')

    def test_buckets_deletion_when_no_buckets_exist(self):
        deleted_buckets = self.client.delete_buckets()
        assert len(deleted_buckets) == 0

    def test_bucket_save(self):
        self.client.create_bucket('mozilla', permissions={'write': ['alexis']})
        bucket = self.client.get_bucket('mozilla')
        assert 'alexis' in bucket['permissions']['write']

    def test_group_creation(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]},
                                 permissions={'write': [
                                     'blah',
                                 ]})
        # Test retrieval of a group gets the permissions as well.
        group = self.client.get_group('payments', bucket='mozilla')
        assert 'blah' in group['permissions']['write']

    def test_group_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.create_group('payments',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]},
                                 permissions={'write': [
                                     'blah',
                                 ]},
                                 if_not_exists=True)

    def test_group_creation_if_bucket_does_not_exist(self):
        with pytest.raises(KintoException):
            self.client.create_group('payments',
                                     bucket='mozilla',
                                     data={'members': [
                                         'blah',
                                     ]})
            self.client.create_group('payments',
                                     bucket='mozilla',
                                     data={'members': [
                                         'blah',
                                     ]},
                                     if_not_exists=True)

    def test_group_update(self):
        self.client.create_bucket('mozilla')
        group = self.client.create_group('payments',
                                         bucket='mozilla',
                                         data={'members': [
                                             'blah',
                                         ]},
                                         if_not_exists=True)
        assert group['data']['members'][0] == 'blah'
        group = self.client.update_group(data={'members': ['blah', 'foo']},
                                         group='payments',
                                         bucket='mozilla')
        self.assertEquals(group['data']['members'][1], 'foo')

    def test_group_list(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('receipts',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.create_group('assets',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        # The returned groups should be strings.
        groups = self.client.get_groups('mozilla')
        self.assertEquals(2, len(groups))
        self.assertEquals(set([coll['id'] for coll in groups]),
                          set(['receipts', 'assets']))

    def test_group_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.delete_group('payments', bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_group_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.delete_group('payments', bucket='mozilla')
        self.client.delete_group('payments', bucket='mozilla', if_exists=True)

    def test_group_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session,
                               'request',
                               side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_group('payments',
                                         bucket='mozilla',
                                         if_exists=True)

    def test_groups_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('amo',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.create_group('blocklist',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.delete_groups(bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_groups_deletion_when_no_groups_exist(self):
        self.client.create_bucket('mozilla')
        deleted_groups = self.client.delete_groups(bucket='mozilla')
        assert len(deleted_groups) == 0

    def test_collection_creation(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments',
                                      bucket='mozilla',
                                      permissions={'write': [
                                          'alexis',
                                      ]})

        # Test retrieval of a collection gets the permissions as well.
        collection = self.client.get_collection('payments', bucket='mozilla')
        assert 'alexis' in collection['permissions']['write']

    def test_collection_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        # Should not raise.
        self.client.create_collection('payments',
                                      bucket='mozilla',
                                      if_not_exists=True)

    def test_collection_list(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('receipts', bucket='mozilla')
        self.client.create_collection('assets', bucket='mozilla')

        # The returned collections should be strings.
        collections = self.client.get_collections('mozilla')
        self.assertEquals(2, len(collections))

        self.assertEquals(set([coll['id'] for coll in collections]),
                          set(['receipts', 'assets']))

    def test_collection_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments', bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collection_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments',
                                      bucket='mozilla',
                                      if_exists=True)

    def test_collection_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session,
                               'request',
                               side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_collection('payments',
                                              bucket='mozilla',
                                              if_exists=True)

    def test_collections_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('amo', bucket='mozilla')
        self.client.create_collection('blocklist', bucket='mozilla')
        self.client.delete_collections(bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collections_deletion_when_no_collections_exist(self):
        self.client.create_bucket('mozilla')
        deleted_collections = self.client.delete_collections(bucket='mozilla')
        assert len(deleted_collections) == 0

    def test_record_creation_and_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})
        record = client.get_record(created['data']['id'])
        assert 'alexis' in record['permissions']['read']

    def test_records_list_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record(data={'foo': 'bar'},
                             permissions={'read': ['alexis']})
        records = client.get_records()
        assert len(records) == 1

    def test_records_paginated_list_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        for i in range(10):
            client.create_record(data={'foo': 'bar'},
                                 permissions={'read': ['alexis']})
        # Kinto is running with kinto.paginate_by = 5
        records = client.get_records()
        assert len(records) == 10

    def test_single_record_save(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})
        created['data']['bar'] = 'baz'

        # XXX enhance this in order to have to pass only one argument, created.
        client.update_record(id=created['data']['id'], data=created['data'])

        retrieved = client.get_record(created['data']['id'])
        assert 'alexis' in retrieved['permissions']['read']
        assert retrieved['data']['foo'] == u'bar'
        assert retrieved['data']['bar'] == u'baz'
        assert created['data']['id'] == retrieved['data']['id']

    def test_single_record_doesnt_overwrite(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})

        with self.assertRaises(KintoException):
            # Create a second record with the ID of the first one.
            client.create_record(data={
                'id': created['data']['id'],
                'bar': 'baz'
            })

    def test_single_record_creation_if_not_exists(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'})
        client.create_record(data={
            'id': created['data']['id'],
            'bar': 'baz'
        },
                             if_not_exists=True)

    def test_single_record_can_overwrite(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})

        client.create_record(data={
            'id': created['data']['id'],
            'bar': 'baz'
        },
                             safe=False)

    def test_one_record_deletion(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record({'foo': 'bar'})
        deleted = client.delete_record(record['data']['id'])
        assert deleted['deleted'] is True
        assert len(client.get_records()) == 0

    def test_record_deletion_if_exists(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record({'foo': 'bar'})
        deleted = client.delete_record(record['data']['id'])
        deleted_if_exists = client.delete_record(record['data']['id'],
                                                 if_exists=True)
        assert deleted['deleted'] is True
        assert deleted_if_exists is None

    def test_multiple_record_deletion(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record({'foo': 'bar'})
        client.delete_records()
        assert len(client.get_records()) == 0

    def test_records_deletion_when_no_records_exist(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        deleted_records = client.delete_records()
        assert len(deleted_records) == 0

    def test_bucket_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a bucket and share it with alice.
        self.client.create_bucket('shared-bucket',
                                  permissions={'read': [
                                      alice_userid,
                                  ]})

        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_bucket('shared-bucket')

    def test_updating_data_on_a_group(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla')
        client.create_bucket()
        client.create_group('payments', data={'members': []})
        client.patch_group('payments', data={'secret': 'psssssst!'})
        group = client.get_group('payments')
        assert group['data']['secret'] == 'psssssst!'

    def test_updating_data_on_a_collection(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()

        client.patch_collection(data={'secret': 'psssssst!'})
        collection = client.get_collection()
        assert collection['data']['secret'] == 'psssssst!'

    def test_collection_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        self.client.create_bucket('bob-bucket')
        self.client.create_collection('shared',
                                      bucket='bob-bucket',
                                      permissions={'read': [
                                          alice_userid,
                                      ]})

        # Try to read the collection as Alice.
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_collection('shared', bucket='bob-bucket')

    def test_record_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a record, and share it with Alice.
        self.client.create_bucket('bob-bucket')
        self.client.create_collection('bob-personal-collection',
                                      bucket='bob-bucket')
        record = self.client.create_record(
            data={'foo': 'bar'},
            permissions={'read': [
                alice_userid,
            ]},
            bucket='bob-bucket',
            collection='bob-personal-collection')

        # Try to read the record as Alice
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        record = alice_client.get_record(id=record['data']['id'],
                                         bucket='bob-bucket',
                                         collection='bob-personal-collection')

        assert record['data']['foo'] == 'bar'

    def test_request_batching(self):
        with self.client.batch(bucket='mozilla', collection='fonts') as batch:
            batch.create_bucket()
            batch.create_collection()
            batch.create_record(data={'foo': 'bar'},
                                permissions={'read': ['natim']})
            batch.create_record(data={'bar': 'baz'},
                                permissions={'read': ['alexis']})

        records = self.client.get_records(bucket='mozilla', collection='fonts')
        assert len(records) == 2

    def test_replication(self):
        # First, create a few records on the first kinto collection.
        with self.client.batch(bucket='origin', collection='coll') as batch:
            batch.create_bucket()
            batch.create_collection()

            for n in range(10):
                batch.create_record(data={'foo': 'bar', 'n': n})

        origin = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='origin',
                        collection='coll')
        destination = Client(server_url=self.server_url,
                             auth=self.auth,
                             bucket='destination',
                             collection='coll')

        replication.replicate(origin, destination)
        records = self.client.get_records(bucket='destination',
                                          collection='coll')
        assert len(records) == 10
Exemplo n.º 26
0
class AppWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setupUi(self)

        ######################################################################################
        ######################################################################################
        self.settings = Settings()
        self.initRep()
        self.initKintoClient()
        self.syncRecipes()
        self.dlgEditG = Dialog(self)
        self.dlgEditH = DialogH(self)
        self.dlgEditD = DialogD(self)
        self.dlgEditY = DialogL(self)
        self.dlgPref = DialogPref(self)
        self.dlgStep = DialogStep(self)
        self.dlgMash = DialogMash(self)

        self.base = ImportBase()
        self.mashProfileExport = ExportMash()

        #        self.base.importBeerXML()
        self.s = 0
        self.recipe = None

        #Les connexions
        self.actionEnregistrer.triggered.connect(self.enregistrer)
        self.actionQuitter.triggered.connect(app.quit)
        #        self.connect(self.actionQuitter, QtCore.SIGNAL("triggered()"), app, QtCore.SLOT("quit()"))

        self.actionShowJournal.triggered.connect(self.showJournal)

        self.actionEditGrains.triggered.connect(self.editGrains)
        self.actionEditHoublons.triggered.connect(self.editHoublons)
        self.actionEditDivers.triggered.connect(self.editDivers)
        self.actionEditLevures.triggered.connect(self.editLevures)
        self.actionRestaurerIngredients.triggered.connect(self.restoreDataBase)
        self.actionImportIng.triggered.connect(self.importIng)
        self.actionManageProfiles.triggered.connect(self.seeMash)

        self.actionAbout.triggered.connect(self.about)

        self.actionAllTools.triggered.connect(self.showTools)

        self.actionPreferences.triggered.connect(self.dialogPreferences)

        #######################################################################################################
        # Profil de brassage       #########################################################################################################

        self.listWidgetSteps.itemSelectionChanged.connect(self.stepDetails)
        self.listWidgetMashProfiles.itemSelectionChanged.connect(
            self.mashClicked)
        self.buttonBoxMashDetails.rejected.connect(self.mashRejected)
        #        self.comboBoxStepType.addItems(["Infusion", "Température", "Décoction"])
        self.pushButtonStepEdit.clicked.connect(self.stepEdit)
        self.dlgStep.stepChanged.connect(self.stepReload)
        self.pushButtonStepRemove.clicked.connect(self.removeStep)
        self.pushButtonNewStep.clicked.connect(self.addStep)
        self.pushButtonMashEdit.clicked.connect(self.mashEdit)
        self.dlgMash.mashChanged.connect(self.mashReload)
        self.pushButtonNewProfile.clicked.connect(self.addMash)
        self.pushButtonRemoveProfile.clicked.connect(self.removeMash)
        self.pushButtonSaveProfile.clicked.connect(self.saveProfile)

        #La bibliotheque
        ###################################################################################################################
        ###################################################################################################################

        # self.listdir(recettes_dir)
        self.showLib()

        ###################################################################################################
        ######## gestion des arguments au lancement du programme  #########################################

        argumentsList = QtWidgets.QApplication.arguments()
        if len(argumentsList) > 1:
            logger.debug("la liste d'arguments: %s", argumentsList)
            logger.debug("le chemin: %s", argumentsList[1])
            # for part in argumentsList :
            #     recipePath=recipePath + " " + part
            try:
                recipePath = argumentsList[1]
                for part in argumentsList[2:]:
                    recipePath = recipePath + " " + part

                self.openRecipeFile(recipePath)
            except:
                pass
        else:
            pass

########################################################################################################################
####################################################################################################################
# le signal émit à la fermeture de la fenêtre de préférences
        self.dlgPref.prefAccepted.connect(self.prefReload)

###########################################################
############### Journal ##############################
######################################################

    def loadJournal(self):
        self.journal = Journal()
        self.journal.loadJournal()
        # self.actionEditJournal.setEnabled(True)

    @QtCore.pyqtSlot()
    def showJournal(self, entry=" '' "):
        self.stackedWidget.setCurrentIndex(0)
        self.loadJournal()
        pyDir = os.path.abspath(os.path.dirname(__file__))
        baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/"))
        self.webViewBiblio.setHtml(self.journal.export("html", entry), baseUrl)
        self.webViewBiblio.page().mainFrame().addToJavaScriptWindowObject(
            "main", self)
        # self.webViewBiblio.page().settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
        # self.webInspector = QtWebKit.QWebInspector(self)
        # self.webInspector.setPage(self.webViewBiblio.page())
        # self.webInspector.setVisible(True)
        # self.verticalLayout_13.addWidget(self.webInspector)

    @QtCore.pyqtSlot(str, str)
    def addToJournal(self, event, recipeName):
        self.loadJournal()
        entry = '''{recipe:%s,date:%s,event:%s,editing:'True'} ''' % (
            "'" + recipeName + "'", "'" + str(int(time.time())) + "'",
            "'" + self.journal.eventsLabels[event] + "'")
        self.showJournal(entry)

    @QtCore.pyqtSlot(str)
    def dumpJournal(self, journalJson):
        journalJson = '{"name":"journal","items": %s }' % journalJson
        d = json.loads(journalJson)
        with open(journal_file, mode="w", encoding="utf-8") as f:
            json.dump(d, f, indent=2)

############## Bibliothèque ##############################
##########################################################

    @QtCore.pyqtSlot()
    def showLib(self):
        # data = json.dumps(self.recipesSummary)
        # data = data.replace("'","&#39;")
        self.stackedWidget.setCurrentIndex(0)
        self.brewdayLock = 0

        self.webSettings = self.webViewBiblio.settings()
        self.webSettings.setAttribute(
            QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True)

        pyDir = os.path.abspath(os.path.dirname(__file__))
        baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(
            pyDir, "static/html/"))
        self.webViewBiblio.setHtml(LibExporterRepository['html'](), baseUrl)
        self.webViewBiblio.page().mainFrame().addToJavaScriptWindowObject(
            "main", self)
        self.webViewBiblio.page().settings().setAttribute(
            QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
        self.webViewBiblio.page().action(
            QtWebKitWidgets.QWebPage.Reload).setVisible(False)

    @QtCore.pyqtSlot(str)
    def deleteLib(self, path):
        confirmation = QtWidgets.QMessageBox.question(
            self, self.tr("Supprimer"),
            self.tr(
                "La recette sera définitivement supprimée <br/> Continuer ?"),
            QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
        if (confirmation == QtWidgets.QMessageBox.Yes):
            os.remove(path)
            self.listdir(recettes_dir)
            self.showLib()
        else:
            self.showLib()

    @QtCore.pyqtSlot()
    def backWebViewBiblio(self):
        self.stackedWidget.setCurrentIndex(0)

    @QtCore.pyqtSlot(str, str)
    def saveRecipe(self, recipe, path):
        logger.debug(path)
        recipeFile = QtCore.QFile(path)
        if recipeFile.open(QtCore.QIODevice.WriteOnly):
            try:
                stream = QtCore.QTextStream(recipeFile)
                stream.setCodec("UTF-8")
                stream << recipe
            finally:
                recipeFile.close()
        else:
            # TODO : Prévenir l'utilisateur en cas d'échec de l'enregistrement
            pass

    @QtCore.pyqtSlot(result=str)
    def createPath(self, file_id=None):
        if file_id is None:
            file_id = str(int(time.time() * 10))
        path = recettes_dir + "/" + file_id + ".xml"
        logger.debug(path)
        return path

    @QtCore.pyqtSlot()
    def resetLock(self):
        self.brewdayLock = 0

############# Mode Brassage ################################
############################################################

    @QtCore.pyqtSlot(str)
    def showBrewdayMode(self, data):
        if self.brewdayLock == 0:
            self.stackedWidget.setCurrentIndex(1)
            self.brewdayLock = 1
            data = data.replace("'", "&#39;")
            pyDir = os.path.abspath(os.path.dirname(__file__))
            baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/"))
            self.webViewBrewday.setHtml(
                BrewdayExporterRepository['html'](data), baseUrl)
            self.webViewBrewday.page().mainFrame().addToJavaScriptWindowObject(
                "main", self)
            self.webViewBrewday.page().settings().setAttribute(
                QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
            self.webViewBrewday.page().action(
                QtWebKitWidgets.QWebPage.Reload).setVisible(False)
        else:
            self.stackedWidget.setCurrentIndex(1)


###### Outils ############################################
##########################################################

    @QtCore.pyqtSlot()
    def showTools(self):
        self.stackedWidget.setCurrentIndex(0)
        pyDir = os.path.abspath(os.path.dirname(__file__))
        baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/"))
        self.webViewBiblio.setHtml(ToolExporterRepository["html"](), baseUrl)
        self.webViewBiblio.page().mainFrame().addToJavaScriptWindowObject(
            "main", self)
        # self.webViewBiblio.page().settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
        # self.webInspector = QtWebKit.QWebInspector(self)
        # self.webInspector.setPage(self.webViewBiblio.page())
        # self.webInspector.setVisible(True)
        # self.verticalLayout_13.addWidget(self.webInspector)

    @QtCore.pyqtSlot(result=str)
    def dataRecipes(self):
        # f = open(recipeData_file, 'w')
        # f.write(self.recipesSummary)
        self.listdir(recettes_dir)
        return self.recipesSummary

    @QtCore.pyqtSlot(result=str)
    def dataProfiles(self):
        return self.mashProfileExport.exportJson(ImportBase().listeMashes)

    @QtCore.pyqtSlot(result=str)
    def dataIngredients(self):
        return ImportBase().exportjson()

    @QtCore.pyqtSlot(result=str)
    def dataPref(self):
        dic = {}
        dic["boilOffRate"] = settings.conf.value("BoilOffRate")
        dic["coolingLossRate"] = settings.conf.value("CoolingLoss")
        dic["grainTemp"] = settings.conf.value("GrainTemp")
        dic["fudgeFactor"] = settings.conf.value("FudgeFactor")
        dic["grainRetention"] = settings.conf.value("GrainRetention")
        dic = json.dumps(dic)
        return dic

    #Une fonction qui gère l'aperçu des couleurs.
    #Contient un tupple avec plusieurs références de couleurs, classées par rang selon la valeur SRM.
    #################################################################################################
    # def colorPreview (self) :
    #     self.colorTuppleSrm = ('FFE699', 'FFD878', 'FFCA5A', 'FFBF42', 'FBB123', 'F8A600', 'F39C00', 'EA8F00', 'E58500', 'DE7C00', 'D77200', 'CF6900', 'CB6200', 'C35900','BB5100', 'B54C00', 'B04500', 'A63E00', 'A13700', '9B3200', '952D00', '8E2900', '882300', '821E00', '7B1A00', '771900', '701400', '6A0E00', '660D00','5E0B00','5A0A02','600903', '520907', '4C0505', '470606', '440607', '3F0708', '3B0607', '3A070B', '36080A')

    #     colorRef= round(self.recipe.compute_EBC()/1.97)

    #     if colorRef >= 30 :
    #         color = "#" + self.colorTuppleSrm[30]
    #     elif colorRef <= 1 :
    #         color = "#" + self.colorTuppleSrm[0]
    #     else :
    #         color = "#" + self.colorTuppleSrm[colorRef-1]
    #     self.widgetColor.setStyleSheet("background-color :" + color)

    def liste_fichiers_recettes(self, rootdir):
        for root, subFolders, files in os.walk(rootdir):
            for file2 in files:
                yield (file2, os.path.join(root, file2))

    def listdir(self, rootdir):
        summaries = []
        for filename, recipe in self.liste_fichiers_recettes(rootdir):
            try:
                summaries.append(self.jsonRecipeLib(recipe))
            except:
                logger.debug("le fichier %s n'est pas une recette" % (recipe))
        self.recipesSummary = "[" + ",".join(summaries) + "]"
        logger.debug("%s recettes détectées" % (len(summaries)))

    def jsonRecipeLib(self, recipe):
        self.s = recipe
        self.recipe = Recipe.parse(recipe)
        data = self.recipe.export("json")
        data = data[1:-1]
        return data

    def initRep(self):
        home = QtCore.QDir(home_dir)
        config = QtCore.QDir(config_dir)
        logger.debug(config)
        if not config.exists():
            home.mkpath(config_dir)
        else:
            pass
        database = QtCore.QFile(database_file)
        if not database.exists():
            database.copy(database_root, database_file)
        else:
            pass
        recettes = QtCore.QFile(recettes_dir)
        if not recettes.exists():
            try:
                shutil.copytree(samples_dir, samples_target)
            except:
                home.mkpath(recettes_dir)
        mash = QtCore.QFile(mash_file)
        if not mash.exists():
            mash.copy(mash_root, mash_file)
        else:
            pass
        journal = QtCore.QFile(journal_file)
        if not journal.exists():
            journal.copy(journal_root, journal_file)
        else:
            pass

        # on configure des valeurs par défaut
        if not settings.conf.contains("BoilOffRate"):
            settings.conf.setValue("BoilOffRate", 10)
        if not settings.conf.contains("CoolingLoss"):
            settings.conf.setValue("CoolingLoss", 5)
        if not settings.conf.contains("GrainTemp"):
            settings.conf.setValue("GrainTemp", 20)
        if not settings.conf.contains("FudgeFactor"):
            settings.conf.setValue("FudgeFactor", 1.7)
        if not settings.conf.contains("GrainRetention"):
            settings.conf.setValue("GrainRetention", 1)
        if not settings.conf.contains("Menus"):
            settings.conf.setValue("Menus", "button")

    def prefReload(self):
        if platform == 'win32':
            recettes_dir = settings.conf.value("pathWin32")
        else:
            recettes_dir = settings.conf.value("pathUnix")
        self.initRep()
        self.initKintoClient()
        self.listdir(recettes_dir)
        self.showLib()

    def initKintoClient(self):
        self.kinto_client = None
        kinto_url = settings.conf.value("KintoServerUrl")
        if kinto_url is not None and kinto_url != "":
            kinto_bucket = settings.conf.value("KintoDefaultBucket")
            kinto_credentials = (settings.conf.value("KintoBasicUserCred"),
                                 settings.conf.value("KintoBasicPasswordCred"))
            logger.debug(kinto_credentials)
            if kinto_bucket is None or kinto_bucket == "":
                kinto_bucket = "joliebulle"
                logger.debug("using default bucket 'joliebulle'")
            try:
                tmp_client = Client(server_url=kinto_url,
                                    auth=kinto_credentials)
                tmp_client.create_bucket(id=kinto_bucket, if_not_exists=True)
                self.kinto_client = Client(server_url=kinto_url,
                                           bucket=kinto_bucket,
                                           auth=kinto_credentials)

                # Création des collections
                self.kinto_client.create_collection(id='recipes',
                                                    if_not_exists=True)
                self.kinto_client.create_collection(id='ingredients',
                                                    if_not_exists=True)

                logger.info("Synchronize with kinto server at :" +
                            repr(self.kinto_client))
            except Exception as e:
                logger.warn("Failed to initialize Kinto synchronisation: " +
                            repr(e))

    def syncRecipes(self):
        recipes_collection = 'recipes'
        if self.kinto_client is None:
            return

        # Sync local recipes
        id_recettes_locales = []
        for filename, full_filename in self.liste_fichiers_recettes(
                recettes_dir):
            recipe_id = filename.replace('.xml', '')
            id_recettes_locales.append(recipe_id)
            try:
                remote_recipe = self.kinto_client.get_record(
                    id=recipe_id, collection=recipes_collection)
                logger.debug("recipe " + recipe_id +
                             " already exists on server, update if needed")
                remote_timestamp = int(remote_recipe['data']['last_modified'] /
                                       1000)
                local_timestamp = int(os.path.getmtime(full_filename))
                logger.debug(
                    str(remote_timestamp) + " " + str(local_timestamp))
                if remote_timestamp < local_timestamp:
                    logger.info("Mise à jour de la recette distante " +
                                recipe_id)
                    # La recette distant doit être mise à jour
                    local_recipe = Recipe.parse(full_filename)
                    data = local_recipe.export("dict")
                    ret = self.kinto_client.update_record(
                        id=recipe_id, collection=recipes_collection, data=data)
                    new_timestamp = ret['data']['last_modified']
                    #MAJ du mtime du fichier pour marquer la synchronisation
                    os.utime(full_filename,
                             times=(int(new_timestamp / 1000),
                                    int(new_timestamp / 1000)))
                if remote_timestamp > local_timestamp:
                    # La recette distante doit être mise à jour
                    logger.info("Mise à jour de la recette locale " +
                                recipe_id)
                    new_recipe = Recipe.parse(remote_recipe['data'], "dict")
                    self.doEnregistrerRecette(new_recipe, full_filename)
                    os.utime(full_filename,
                             times=(int(remote_timestamp),
                                    int(remote_timestamp)))
            except KintoException:
                logger.debug("recipe " + recipe_id +
                             " doesn't exists on server, sending it")
                new_recipe = Recipe.parse(full_filename)
                data = new_recipe.export("dict")
                ret = self.kinto_client.create_record(
                    id=recipe_id, collection=recipes_collection, data=data)
                new_timestamp = ret['data']['last_modified']
                #MAJ du mtime du fichier pour marquer la synchronisation
                os.utime(full_filename,
                         times=(int(new_timestamp / 1000),
                                int(new_timestamp / 1000)))

        recipes = self.kinto_client.get_records(collection='recipes')
        for recipe in recipes:
            if recipe['id'] not in id_recettes_locales:
                #Recette distante non présente localement
                new_recipe = Recipe.parse(recipe, "dict")
                fullname = recipe['id'] + ".xml"
                logger.debug("Création de la recette " + fullname)
                self.doEnregistrerRecette(new_recipe,
                                          os.path.join(recettes_dir, fullname))
                os.utime(full_filename,
                         times=(int(remote_timestamp), int(remote_timestamp)))

    @QtCore.pyqtSlot()
    def switchToLibrary(self):
        self.stackedWidget.setCurrentIndex(0)
        # self.viewRecipeLib(self.s)

    def switchToMash(self):
        self.stackedWidget.setCurrentIndex(2)

    def restoreDataBase(self):
        home = QtCore.QDir(home_dir)
        config = QtCore.QDir(config_dir)
        database = QtCore.QFile(database_file)
        confirmation = QtWidgets.QMessageBox.question(
            self, self.tr("Remplacer la base ?"),
            self.
            tr("La base des ingrédients actuelle va être effacée et remplacée par la base originale. Toutes vos modifications vont être effacées. Un redémarrage de l'application sera nécessaire.<br> Continuer ?"
               ), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
        if (confirmation == QtWidgets.QMessageBox.Yes):
            database.remove(database_file)
            database.copy(database_root, database_file)
        else:

            pass

    def editGrains(self):
        self.dlgEditG.setModal(True)
        self.dlgEditG.setModel()
        self.dlgEditG.show()

    def editHoublons(self):
        self.dlgEditH.setModal(True)
        self.dlgEditH.setModel()
        self.dlgEditH.show()

    def editDivers(self):
        self.dlgEditD.setModal(True)
        self.dlgEditD.setModel()
        self.dlgEditD.show()

    def editLevures(self):
        self.dlgEditY.setModal(True)
        self.dlgEditY.setModel()
        self.dlgEditY.show()

    @QtCore.pyqtSlot(float, float, float, float)
    def preBoilCheck(self, volPreBoil, preBoilSg, GU, volume):
        self.dlgPreBoil = DialogPreBoil(self)
        self.dlgPreBoil.setData(volPreBoil, preBoilSg, GU, volume)
        self.dlgPreBoil.setModal(True)
        self.dlgPreBoil.show()

    def dialogPreferences(self):
        self.dlgPref.setModal(True)
        self.dlgPref.show()

    def importBeerXML(self):
        fichierBeerXML = self.s
        try:
            self.recipe = Recipe.parse(fichierBeerXML)
            self.currentRecipeMash = self.recipe.mash

        except:
            errors = Errors()
            errors.warningXml()

    def about(self):
        about = DialogAbout(self)
        about.show()

    def doEnregistrerRecette(self, recipe, destination):
        recipeFile = QtCore.QFile(destination)
        if recipeFile.open(QtCore.QIODevice.WriteOnly):
            try:
                stream = QtCore.QTextStream(recipeFile)
                stream.setCodec("UTF-8")
                stream << recipe.export("beerxml")
            finally:
                recipeFile.close()
        else:
            # TODO : Prévenir l'utilisateur en cas d'échec de l'enregistrement
            pass

    def enregistrerRecette(self, destination):
        self.doEnregistrerRecette(self.recipe, destination)
        self.fileSaved = True

    def enregistrer(self):
        if self.recipe.name != self.lineEditRecette.text():
            self.nameChanged = True
        else:
            self.nameChanged = False

        self.recipe.name = self.lineEditRecette.text()
        self.recipe.style = self.lineEditGenre.text()
        self.recipe.brewer = self.lineEditBrewer.text()
        self.recipe.boil = self.spinBoxBoil.value()
        if not self.s:
            destination = recettes_dir + "/" + self.recipe.name.replace(
                '/', ' ') + ".xml"
            if os.path.exists(destination):
                errors = Errors()
                errors.warningExistingPath()
                self.fileSaved = False
            else:
                self.s = destination
                self.enregistrerRecette(destination)
        else:
            self.enregistrerRecette(self.s)

    def enregistrerSous(self):
        self.s = QtGui.QFileDialog.getSaveFileName(
            self, self.tr("Enregistrer dans un fichier"),
            recettes_dir + "/" + self.recipe.name.replace('/', ' ') + ".xml",
            "BeerXML (*.xml)")
        self.enregistrerRecette(self.s)

    @QtCore.pyqtSlot(str)
    def copyBbcode(self, bbcode):
        app.clipboard().setText(bbcode)

    def importIng(self):
        s = QtWidgets.QFileDialog.getOpenFileName(
            self,
            self.tr("Ouvrir un fichier"),
            home_dir,
        )
        if not s:
            pass
        else:
            self.importIngList = ImportIng()
            self.importIngList.parseFile(s)

    def mashComboChanged(self):
        #on remet le verrou à 0, il va falloir recalculer en repassant en brewday mode
        self.brewdayLock = 0
        try:
            i = self.comboBoxMashProfiles.currentIndex()
            self.currentMash = ImportBase().listeMashes[i]
        except:
            self.currentMash = self.currentRecipeMash
        if i == -1:
            self.currentMash = Mash()
        self.recipe.mash = self.currentMash

    def seeMash(self):
        self.switchToMash()
        index = self.listWidgetMashProfiles.currentRow()
        i = self.listWidgetSteps.currentRow()
        self.listWidgetMashProfiles.clear()
        self.listWidgetSteps.clear()

        self.numMash = len(ImportBase().listeMashes)
        #self.numSteps = self.mashProfilesBase.numSteps
        self.popMashList()
        self.pushButtonMashEdit.setEnabled(False)
        self.pushButtonRemoveProfile.setEnabled(False)
        self.pushButtonStepRemove.setEnabled(False)
        self.pushButtonStepEdit.setEnabled(False)
        self.listWidgetMashProfiles.setCurrentRow(index)
        self.listWidgetSteps.setCurrentRow(i)

    def popMashList(self):
        self.listWidgetMashProfiles.clear()
        for mash in ImportBase().listeMashes:
            self.listWidgetMashProfiles.addItem(mash.name)

    def mashClicked(self):
        self.listWidgetSteps.clear()
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            mash = ImportBase().listeMashes[index]
            for step in mash.listeSteps:
                self.listWidgetSteps.addItem(step.name)

            self.labelStepName.setTextFormat(QtCore.Qt.RichText)
            self.labelMashName.setText("<b>" + mash.name + "</b>")
            self.labelMashPh.setText("%.1f" % float(mash.ph))
            #        self.labelMashGrainTemp.setText("%.1f" %float(self.dicMashDetail['grainTemp']))
            #        self.labelMashTunTemp.setText("%.1f" %float(self.dicMashDetail['tunTemp']))
            try:
                self.labelMashSpargeTemp.setText("%.1f" %
                                                 float(mash.spargeTemp))
            except:
                pass
            try:
                self.listWidgetSteps.setCurrentRow(0)
            except:
                pass

    #        print(self.dicMashDetail)
            self.pushButtonMashEdit.setEnabled(True)
            self.pushButtonRemoveProfile.setEnabled(True)

    def mashDetails(self):
        self.dlgMashDetail = DialogMashDetail(self)
        self.dlgMashDetail.setModal(True)
        self.dlgMashDetail.show()
        self.dlgMashDetail.setFields(self.currentMash)
        self.dlgMashDetail.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)

    def stepDetails(self):
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                try:
                    selected_step = selected_mash.listeSteps[i]
                    self.labelStepName.setTextFormat(QtCore.Qt.RichText)
                    self.labelStepName.setText("<b>" + selected_step.name +
                                               "</b>")
                    self.labelStepType.setText(selected_step.type)
                    self.labelStepTemp.setText(
                        MashStepView.temp_to_display(selected_step.temp))
                    self.labelStepTime.setText(
                        MashStepView.time_to_display(selected_step.time))
                    self.pushButtonStepRemove.setEnabled(True)
                    self.pushButtonStepEdit.setEnabled(True)
                except:
                    pass

    def stepEdit(self):
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                selected_step = selected_mash.listeSteps[i]

                self.dlgStep.show()
                self.dlgStep.fields(selected_step)

    def stepReload(self, step):
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                selected_step = selected_mash.listeSteps[i]

                selected_step.name = step.name
                selected_step.type = step.type
                selected_step.temp = step.temp
                selected_step.time = step.time
                self.seeMash()
                self.stepDetails()
                self.listWidgetMashProfiles.setCurrentRow(index)
                self.listWidgetSteps.setCurrentRow(i)

    def removeStep(self):
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                item = self.listWidgetSteps.currentItem()
                del selected_mash.listeSteps[i]
                # self.listWidgetSteps.clearSelection()
                #self.listWidgetSteps.takeItem(item)
                #On force la sélection sur la ligne précédente
                self.listWidgetSteps.setCurrentRow(i - 1)
                self.seeMash()

    def addStep(self):
        index = self.listWidgetMashProfiles.currentRow()
        selected_mash = ImportBase().listeMashes[index]
        i = self.listWidgetSteps.currentRow()
        step = MashStep()
        step.name = 'Nouveau palier'
        step.type = 'Infusion'
        step.time = '0'
        step.temp = '0'
        step.vol = '0'
        selected_mash.listeSteps.append(step)

        self.listWidgetMashProfiles.setCurrentRow(index)
        self.seeMash()
        self.stepDetails()
        self.listWidgetMashProfiles.setCurrentRow(index)
        # self.listWidgetSteps.setCurrentRow(i-1)
        # self.stepEdit()

    def mashEdit(self):
        index = self.listWidgetMashProfiles.currentRow()
        selected_mash = ImportBase().listeMashes[index]
        self.dlgMash.show()
        self.dlgMash.fields(selected_mash)

    def mashReload(self, mash):
        #on remet le verrou à 0, il va falloir recalculer en repassant en brewday mode
        self.brewdayLock = 0
        f = self.listWidgetMashProfiles.currentRow()
        selected_mash = ImportBase().listeMashes[f]
        selected_mash.name = mash.name
        selected_mash.ph = mash.ph
        selected_mash.grainTemp = 20
        selected_mash.tunTemp = 20
        selected_mash.spargeTemp = mash.spargeTemp
        self.popMashList()
        self.listWidgetMashProfiles.setCurrentRow(f)

    def addMash(self):
        new_mash = Mash()
        new_mash.name = 'Nouveau profil'
        new_mash.grainTemp = '0'
        new_mash.tunTemp = '0'
        new_mash.spargeTemp = '78'
        new_mash.ph = 5.4
        new_step = MashStep()
        new_step.name = 'Nouveau Palier'
        new_step.type = 'Infusion'
        new_step.time = '0'
        new_step.temp = '0'
        new_mash.listeSteps.append(new_step)
        ImportBase().listeMashes.append(new_mash)
        self.seeMash()
        self.listWidgetMashProfiles.setCurrentRow(
            len(ImportBase().listeMashes) - 1)

    def removeMash(self):
        i = self.listWidgetMashProfiles.currentRow()
        del ImportBase().listeMashes[i]
        self.seeMash()
        self.listWidgetSteps.clear()

    def mashRejected(self):
        self.showLib()

    def saveProfile(self):
        self.mashProfileExport.export(ImportBase().listeMashes)
        self.mashProfileExport.enregistrer(mash_file)

    @QtCore.pyqtSlot()
    def printRecipe(self):
        printer = QtPrintSupport.QPrinter()
        dialog = QtPrintSupport.QPrintDialog(printer)
        dialog.setModal(True)
        dialog.setWindowTitle("Print Document")
        if dialog.exec_() == True:
            self.webViewBiblio.print(printer)
            # document=QtGui.QTextDocument()
            # stringHtml=self.recipe.export("print")
            # document.setHtml(stringHtml)
            # document.print(printer)

    @QtCore.pyqtSlot()
    def printBrewday(self):
        printer = QtPrintSupport.QPrinter()
        dialog = QtPrintSupport.QPrintDialog(printer)
        dialog.setModal(True)
        dialog.setWindowTitle("Print Document")
        if dialog.exec_() == True:
            self.webViewBrewday.print(printer)
Exemplo n.º 27
0
def main():
    args = _get_args()

    client = Client(server_url=args.server,
                    auth=tuple(args.auth.split(':')),
                    bucket=args.source_bucket,
                    collection=args.source_col)

    if args.editor_auth is None:
        args.editor_auth = args.auth

    if args.reviewer_auth is None:
        args.reviewer_auth = args.auth

    editor_client = Client(server_url=args.server,
                           auth=tuple(args.editor_auth.split(':')),
                           bucket=args.source_bucket,
                           collection=args.source_col)
    reviewer_client = Client(server_url=args.server,
                             auth=tuple(args.reviewer_auth.split(':')),
                             bucket=args.source_bucket,
                             collection=args.source_col)

    # 0. initialize source bucket/collection (if necessary)
    server_info = client.server_info()
    editor_id = editor_client.server_info()['user']['id']
    reviewer_id = reviewer_client.server_info()['user']['id']
    print('Server: {0}'.format(args.server))
    print('Author: {user[id]}'.format(**server_info))
    print('Editor: {0}'.format(editor_id))
    print('Reviewer: {0}'.format(reviewer_id))

    # 0. check that this collection is well configured.
    signer_capabilities = server_info['capabilities']['signer']
    to_review_enabled = signer_capabilities.get('to_review_enabled', False)
    group_check_enabled = signer_capabilities.get('group_check_enabled', False)

    resources = [
        r for r in signer_capabilities['resources']
        if (args.source_bucket, args.source_col) == (r['source']['bucket'],
                                                     r['source']['collection'])
    ]
    assert len(resources) > 0, 'Specified source not configured to be signed'
    resource = resources[0]
    if to_review_enabled and 'preview' in resource:
        print(
            'Signoff: {source[bucket]}/{source[collection]} => {preview[bucket]}/{preview[collection]} => {destination[bucket]}/{destination[collection]}'
            .format(**resource))
    else:
        print(
            'Signoff: {source[bucket]}/{source[collection]} => {destination[bucket]}/{destination[collection]}'
            .format(**resource))
    print('Group check: {0}'.format(group_check_enabled))
    print('Review workflow: {0}'.format(to_review_enabled))

    print('_' * 80)

    bucket = client.create_bucket(if_not_exists=True)
    client.patch_bucket(permissions={
        'write': [editor_id, reviewer_id] + bucket['permissions']['write']
    },
                        if_match=bucket['data']['last_modified'],
                        safe=True)

    client.create_collection(if_not_exists=True)

    if args.reset:
        client.delete_records()
        existing = 0
    else:
        existing_records = client.get_records()
        existing = len(existing_records)

    if group_check_enabled:
        editors_group = signer_capabilities['editors_group']
        client.create_group(editors_group,
                            data={'members': [editor_id]},
                            if_not_exists=True)
        reviewers_group = signer_capabilities['reviewers_group']
        client.create_group(reviewers_group,
                            data={'members': [reviewer_id]},
                            if_not_exists=True)

    dest_client = Client(server_url=args.server,
                         bucket=resource['destination']['bucket'],
                         collection=resource['destination']['collection'])

    preview_client = None
    if to_review_enabled and 'preview' in resource:
        preview_bucket = resource['preview']['bucket']
        preview_collection = resource['preview']['collection']
        preview_client = Client(server_url=args.server,
                                bucket=preview_bucket,
                                collection=preview_collection)

    # 1. upload data
    print('Author uploads 20 random records')
    records = upload_records(client, 20)

    # 2. ask for a signature
    # 2.1 ask for review (noop on old versions)
    print('Editor asks for review')
    data = {"status": "to-review"}
    editor_client.patch_collection(data=data)
    # 2.2 check the preview collection (if enabled)
    if preview_client:
        print('Check preview collection')
        preview_records = preview_client.get_records()
        expected = existing + 20
        assert len(preview_records) == expected, '%s != %s records' % (
            len(preview_records), expected)
        metadata = preview_client.get_collection()['data']
        preview_signature = metadata.get('signature')
        assert preview_signature, 'Preview collection not signed'
        preview_timestamp = collection_timestamp(preview_client)
    # 2.3 approve the review
    print('Reviewer approves and triggers signature')
    data = {"status": "to-sign"}
    reviewer_client.patch_collection(data=data)

    # 3. upload more data
    print('Author creates 20 others records')
    upload_records(client, 20)

    print('Editor updates 5 random records')
    for toupdate in random.sample(records, 5):
        editor_client.patch_record(dict(newkey=_rand(10), **toupdate))

    print('Author deletes 5 random records')
    for todelete in random.sample(records, 5):
        client.delete_record(todelete['id'])

    expected = existing + 20 + 20 - 5

    # 4. ask again for a signature
    # 2.1 ask for review (noop on old versions)
    print('Editor asks for review')
    data = {"status": "to-review"}
    editor_client.patch_collection(data=data)
    # 2.2 check the preview collection (if enabled)
    if preview_client:
        print('Check preview collection')
        preview_records = preview_client.get_records()
        assert len(preview_records) == expected, '%s != %s records' % (
            len(preview_records), expected)
        # Diff size is 20 + 5 if updated records are also all deleted,
        # or 30 if deletions and updates apply to different records.
        diff_since_last = preview_client.get_records(_since=preview_timestamp)
        assert 25 <= len(
            diff_since_last
        ) <= 30, 'Changes since last signature are not consistent'

        metadata = preview_client.get_collection()['data']
        assert preview_signature != metadata[
            'signature'], 'Preview collection not updated'

    # 2.3 approve the review
    print('Reviewer approves and triggers signature')
    data = {"status": "to-sign"}
    reviewer_client.patch_collection(data=data)

    # 5. wait for the result

    # 6. obtain the destination records and serialize canonically.

    records = list(dest_client.get_records())
    assert len(records) == expected, '%s != %s records' % (len(records),
                                                           expected)
    timestamp = collection_timestamp(dest_client)
    serialized = canonical_json(records, timestamp)
    print('Hash is %r' % compute_hash(serialized))

    # 7. get back the signed hash

    dest_col = dest_client.get_collection()
    signature = dest_col['data']['signature']

    with open('pub', 'w') as f:
        f.write(signature['public_key'])

    # 8. verify the signature matches the hash
    signer = ECDSASigner(public_key='pub')
    try:
        signer.verify(serialized, signature)
        print('Signature OK')
    except Exception:
        print('Signature KO')
        raise
Exemplo n.º 28
0
class FunctionalTest(unittest.TestCase):

    def setUp(self):
        super().setUp()
        # XXX Read the configuration from env variables.
        self.server_url = SERVER_URL
        self.auth = DEFAULT_AUTH

        # Read the configuration.
        self.config = configparser.RawConfigParser()
        self.config.read(os.path.join(__HERE__, 'config/kinto.ini'))
        self.client = Client(server_url=self.server_url, auth=self.auth)
        self.create_user(self.auth)

    def tearDown(self):
        # Delete all the created objects
        flush_url = urljoin(self.server_url, '/__flush__')
        resp = requests.post(flush_url)
        resp.raise_for_status()

    def create_user(self, credentials):
        account_url = urljoin(self.server_url, '/accounts/{}'.format(credentials[0]))
        r = requests.put(account_url, json={"data": {"password": credentials[1]}},
                         auth=DEFAULT_AUTH)
        r.raise_for_status()
        return r.json()

    def get_user_id(self, credentials):
        r = self.create_user(credentials)
        return 'account:{}'.format(r["data"]["id"])

    def test_bucket_creation(self):
        bucket = self.client.create_bucket(id='mozilla')
        user_id = self.get_user_id(self.auth)
        assert user_id in bucket['permissions']['write']

    def test_bucket_creation_if_not_exists(self):
        self.client.create_bucket(id='mozilla')
        # Should not raise.
        self.client.create_bucket(id='mozilla', if_not_exists=True)

    def test_buckets_retrieval(self):
        self.client.create_bucket(id='mozilla')
        buckets = self.client.get_buckets()
        assert len(buckets) == 1

    def test_bucket_retrieval(self):
        self.client.create_bucket(id='mozilla')
        self.client.get_bucket(id='mozilla')
        # XXX Add permissions handling during creation and check they are
        # present during retrieval.

    def test_bucket_modification(self):
        bucket = self.client.create_bucket(id='mozilla', data={'version': 1})
        assert bucket['data']['version'] == 1
        bucket = self.client.patch_bucket(id='mozilla', data={'author': 'you'})
        assert bucket['data']['version'] == 1
        assert bucket['data']['author'] == 'you'
        bucket = self.client.update_bucket(id='mozilla', data={'date': 'today'})
        assert bucket['data']['date'] == 'today'
        assert 'version' not in bucket['data']

    def test_bucket_retrieval_fails_when_not_created(self):
        self.assertRaises(BucketNotFound, self.client.get_bucket, id='non-existent')

    def test_bucket_deletion(self):
        self.client.create_bucket(id='mozilla')
        self.client.delete_bucket(id='mozilla')
        self.assertRaises(BucketNotFound, self.client.get_bucket, id='mozilla')

    def test_bucket_deletion_if_exists(self):
        self.client.create_bucket(id='mozilla')
        self.client.delete_bucket(id='mozilla')
        self.client.delete_bucket(id='mozilla', if_exists=True)

    def test_buckets_deletion(self):
        self.client.create_bucket(id='mozilla')
        buckets = self.client.delete_buckets()
        assert buckets[0]['id'] == 'mozilla'
        self.assertRaises(BucketNotFound, self.client.get_bucket, id='mozilla')

    def test_buckets_deletion_when_no_buckets_exist(self):
        deleted_buckets = self.client.delete_buckets()
        assert len(deleted_buckets) == 0

    def test_bucket_save(self):
        self.client.create_bucket(id='mozilla', permissions={'write': ['account:alexis']})
        bucket = self.client.get_bucket(id='mozilla')
        assert 'account:alexis' in bucket['permissions']['write']

    def test_group_creation(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(
            id='payments', bucket='mozilla',
            data={'members': ['blah', ]},
            permissions={'write': ['blah', ]})
        # Test retrieval of a group gets the permissions as well.
        group = self.client.get_group(id='payments', bucket='mozilla')
        assert 'blah' in group['permissions']['write']

    def test_group_creation_if_not_exists(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(id='payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group(
            id='payments', bucket='mozilla',
            data={'members': ['blah', ]},
            permissions={'write': ['blah', ]},
            if_not_exists=True)

    def test_group_creation_if_bucket_does_not_exist(self):
        with pytest.raises(KintoException) as e:
            self.client.create_group(
                id='payments', bucket='mozilla',
                data={'members': ['blah']})
        assert str(e).endswith('PUT /v1/buckets/mozilla/groups/payments - '
                               '403 Unauthorized. Please check that the '
                               'bucket exists and that you have the permission '
                               'to create or write on this group.')

    def test_group_update(self):
        self.client.create_bucket(id='mozilla')
        group = self.client.create_group(
                    id='payments', bucket='mozilla',
                    data={'members': ['blah', ]},
                    if_not_exists=True)
        assert group['data']['members'][0] == 'blah'
        group = self.client.update_group(
                    data={'members': ['blah', 'foo']},
                    id='payments', bucket='mozilla')
        self.assertEqual(group['data']['members'][1], 'foo')

    def test_group_list(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(id='receipts', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group(id='assets', bucket='mozilla', data={'members': ['blah', ]})
        # The returned groups should be strings.
        groups = self.client.get_groups(bucket='mozilla')
        self.assertEqual(2, len(groups))
        self.assertEqual(set([coll['id'] for coll in groups]),
                         set(['receipts', 'assets']))

    def test_group_deletion(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(id='payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_group(id='payments', bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_group_deletion_if_exists(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(id='payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_group(id='payments', bucket='mozilla')
        self.client.delete_group(id='payments', bucket='mozilla', if_exists=True)

    def test_group_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session, 'request', side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_group(id='payments', bucket='mozilla', if_exists=True)

    def test_groups_deletion(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(id='amo', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group(id='blocklist', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_groups(bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_groups_deletion_when_no_groups_exist(self):
        self.client.create_bucket(id='mozilla')
        deleted_groups = self.client.delete_groups(bucket='mozilla')
        assert len(deleted_groups) == 0

    def test_collection_creation(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(
            id='payments', bucket='mozilla',
            permissions={'write': ['account:alexis', ]}
        )

        # Test retrieval of a collection gets the permissions as well.
        collection = self.client.get_collection(id='payments', bucket='mozilla')
        assert 'account:alexis' in collection['permissions']['write']

    def test_collection_not_found(self):
        self.client.create_bucket(id='mozilla')

        with pytest.raises(CollectionNotFound):
            self.client.get_collection(id='payments', bucket='mozilla')

    def test_collection_access_forbidden(self):
        with pytest.raises(KintoException):
            self.client.get_collection(id='payments', bucket='mozilla')

    def test_collection_creation_if_not_exists(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(id='payments', bucket='mozilla')
        # Should not raise.
        self.client.create_collection(id='payments', bucket='mozilla',
                                      if_not_exists=True)

    def test_collection_list(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(id='receipts', bucket='mozilla')
        self.client.create_collection(id='assets', bucket='mozilla')

        # The returned collections should be strings.
        collections = self.client.get_collections(bucket='mozilla')
        self.assertEqual(len(collections), 2)

        self.assertEqual(set([coll['id'] for coll in collections]),
                         set(['receipts', 'assets']))

    def test_collection_deletion(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(id='payments', bucket='mozilla')
        self.client.delete_collection(id='payments', bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collection_deletion_if_exists(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(id='payments', bucket='mozilla')
        self.client.delete_collection(id='payments', bucket='mozilla')
        self.client.delete_collection(id='payments', bucket='mozilla', if_exists=True)

    def test_collection_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session, 'request', side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_collection(id='payments', bucket='mozilla', if_exists=True)

    def test_collections_deletion(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(id='amo', bucket='mozilla')
        self.client.create_collection(id='blocklist', bucket='mozilla')
        self.client.delete_collections(bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collections_deletion_when_no_collections_exist(self):
        self.client.create_bucket(id='mozilla')
        deleted_collections = self.client.delete_collections(bucket='mozilla')
        assert len(deleted_collections) == 0

    def test_record_creation_and_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['account:alexis']})
        record = client.get_record(id=created['data']['id'])
        assert 'account:alexis' in record['permissions']['read']

    def test_records_list_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record(data={'foo': 'bar'},
                             permissions={'read': ['account:alexis']})
        records = client.get_records()
        assert len(records) == 1

    def test_records_timestamp_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={'foo': 'bar'},
                                      permissions={'read': ['account:alexis']})
        etag = client.get_records_timestamp()
        assert str(etag) == str(record["data"]["last_modified"])

    def test_records_paginated_list_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        for i in range(10):
            client.create_record(data={'foo': 'bar'},
                                 permissions={'read': ['account:alexis']})
        # Kinto is running with kinto.paginate_by = 5
        records = client.get_records()
        assert len(records) == 10

    def test_single_record_save(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['account:alexis']})
        created['data']['bar'] = 'baz'

        # XXX enhance this in order to have to pass only one argument, created.
        client.update_record(id=created['data']['id'], data=created['data'])

        retrieved = client.get_record(id=created['data']['id'])
        assert 'account:alexis' in retrieved['permissions']['read']
        assert retrieved['data']['foo'] == u'bar'
        assert retrieved['data']['bar'] == u'baz'
        assert created['data']['id'] == retrieved['data']['id']

    def test_single_record_doesnt_overwrite(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['account:alexis']})

        with self.assertRaises(KintoException):
            # Create a second record with the ID of the first one.
            client.create_record(data={'id': created['data']['id'], 'bar': 'baz'})

    def test_single_record_creation_if_not_exists(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'})
        client.create_record(data={'id': created['data']['id'],
                                   'bar': 'baz'},
                             if_not_exists=True)

    def test_single_record_can_overwrite(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['account:alexis']})

        client.create_record(data={'id': created['data']['id'],
                                   'bar': 'baz'}, safe=False)

    def test_one_record_deletion(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={'foo': 'bar'})
        deleted = client.delete_record(id=record['data']['id'])
        assert deleted['deleted'] is True
        assert len(client.get_records()) == 0

    def test_record_deletion_if_exists(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={'foo': 'bar'})
        deleted = client.delete_record(id=record['data']['id'])
        deleted_if_exists = client.delete_record(id=record['data']['id'], if_exists=True)
        assert deleted['deleted'] is True
        assert deleted_if_exists is None

    def test_multiple_record_deletion(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record(data={'foo': 'bar'})
        client.delete_records()
        assert len(client.get_records()) == 0

    def test_records_deletion_when_no_records_exist(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        deleted_records = client.delete_records()
        assert len(deleted_records) == 0

    def test_bucket_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a bucket and share it with alice.
        self.client.create_bucket(id='shared-bucket',
                                  permissions={'read': [alice_userid, ]})

        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_bucket(id='shared-bucket')

    def test_updating_data_on_a_group(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla')
        client.create_bucket()
        client.create_group(id='payments', data={'members': []})
        client.patch_group(id='payments', data={'secret': 'psssssst!'})
        group = client.get_group(id='payments')
        assert group['data']['secret'] == 'psssssst!'

    def test_updating_data_on_a_collection(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()

        client.patch_collection(data={'secret': 'psssssst!'})
        collection = client.get_collection()
        assert collection['data']['secret'] == 'psssssst!'

    def test_collection_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        self.client.create_bucket(id='bob-bucket')
        self.client.create_collection(
            id='shared',
            bucket='bob-bucket',
            permissions={'read': [alice_userid, ]})

        # Try to read the collection as Alice.
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_collection(id='shared', bucket='bob-bucket')

    def test_record_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a record, and share it with Alice.
        self.client.create_bucket(id='bob-bucket')
        self.client.create_collection(id='bob-personal-collection',
                                      bucket='bob-bucket')
        record = self.client.create_record(
            data={'foo': 'bar'},
            permissions={'read': [alice_userid, ]},
            bucket='bob-bucket',
            collection='bob-personal-collection')

        # Try to read the record as Alice
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        record = alice_client.get_record(
            id=record['data']['id'],
            bucket='bob-bucket',
            collection='bob-personal-collection')

        assert record['data']['foo'] == 'bar'

    def test_request_batching(self):
        with self.client.batch(bucket='mozilla', collection='fonts') as batch:
            batch.create_bucket()
            batch.create_collection()
            batch.create_record(data={'foo': 'bar'},
                                permissions={'read': ['natim']})
            batch.create_record(data={'bar': 'baz'},
                                permissions={'read': ['account:alexis']})

        _, _, r1, r2 = batch.results()
        records = self.client.get_records(bucket='mozilla', collection='fonts')

        assert len(records) == 2
        assert records[0] == r2['data']
        assert records[1] == r1['data']

    def test_patch_record_jsonpatch(self):
        self.client.create_bucket(id='b1')
        self.client.create_collection(id='c1', bucket='b1')
        self.client.create_record(id='r1', collection='c1', bucket='b1',
                                  data={"hello": "world"})
        patch = JSONPatch([
            {'op': 'add', 'path': '/data/goodnight', 'value': 'moon'},
            {'op': 'add', 'path': '/permissions/read/alice'}
        ])
        self.client.patch_record(id='r1', collection='c1', bucket='b1',
                                 changes=patch)
        record = self.client.get_record(bucket='b1', collection='c1', id='r1')
        assert record['data']['hello'] == 'world'
        assert record['data']['goodnight'] == 'moon'
        assert record['permissions']['read'] == ['alice']

    def test_replication(self):
        # First, create a few records on the first kinto collection.
        with self.client.batch(bucket='origin', collection='coll') as batch:
            batch.create_bucket()
            batch.create_collection()

            for n in range(10):
                batch.create_record(data={'foo': 'bar', 'n': n})

        origin = Client(
            server_url=self.server_url,
            auth=self.auth,
            bucket='origin',
            collection='coll'
        )
        destination = Client(
            server_url=self.server_url,
            auth=self.auth,
            bucket='destination',
            collection='coll')

        replication.replicate(origin, destination)
        records = self.client.get_records(bucket='destination',
                                          collection='coll')
        assert len(records) == 10
Exemplo n.º 29
0
def main():
    args = _get_args()

    client = Client(server_url=args.server, auth=tuple(args.auth.split(':')),
                    bucket=args.source_bucket,
                    collection=args.source_col)

    if args.editor_auth is None:
        args.editor_auth = args.auth

    if args.reviewer_auth is None:
        args.reviewer_auth = args.auth

    editor_client = Client(server_url=args.server,
                           auth=tuple(args.editor_auth.split(':')),
                           bucket=args.source_bucket,
                           collection=args.source_col)
    reviewer_client = Client(server_url=args.server,
                             auth=tuple(args.reviewer_auth.split(':')),
                             bucket=args.source_bucket,
                             collection=args.source_col)

    # 0. initialize source bucket/collection (if necessary)
    server_info = client.server_info()
    editor_id = editor_client.server_info()['user']['id']
    reviewer_id = reviewer_client.server_info()['user']['id']
    print('Server: {0}'.format(args.server))
    print('Author: {user[id]}'.format(**server_info))
    print('Editor: {0}'.format(editor_id))
    print('Reviewer: {0}'.format(reviewer_id))

    # 0. check that this collection is well configured.
    signer_capabilities = server_info['capabilities']['signer']

    resources = [r for r in signer_capabilities['resources']
                 if (args.source_bucket, args.source_col) == (r['source']['bucket'], r['source']['collection']) or
                    (args.source_bucket, None) == (r['source']['bucket'], r['source']['collection'])]
    assert len(resources) > 0, 'Specified source not configured to be signed'
    resource = resources[0]
    if 'preview' in resource:
        print('Signoff: {source[bucket]}/{source[collection]} => {preview[bucket]}/{preview[collection]} => {destination[bucket]}/{destination[collection]}'.format(**resource))
    else:
        print('Signoff: {source[bucket]}/{source[collection]} => {destination[bucket]}/{destination[collection]}'.format(**resource))

    print('_' * 80)

    bucket = client.create_bucket(if_not_exists=True)
    client.create_collection(permissions={'write': [editor_id, reviewer_id] + bucket['permissions']['write']}, if_not_exists=True)

    editors_group = resource.get('editors_group') or signer_capabilities['editors_group']
    editors_group = editors_group.format(collection_id=args.source_col)
    client.patch_group(id=editors_group, data={'members': [editor_id]})

    reviewers_group = resource.get('reviewers_group') or signer_capabilities['reviewers_group']
    reviewers_group = reviewers_group.format(collection_id=args.source_col)
    client.patch_group(id=reviewers_group, data={'members': [reviewer_id]})

    if args.reset:
        client.delete_records()
        existing = 0
    else:
        existing_records = client.get_records()
        existing = len(existing_records)

    dest_col = resource['destination'].get('collection') or args.source_col
    dest_client = Client(server_url=args.server,
                         bucket=resource['destination']['bucket'],
                         collection=dest_col)

    preview_client = None
    if 'preview' in resource:
        preview_bucket = resource['preview']['bucket']
        preview_collection = resource['preview'].get('collection') or args.source_col
        preview_client = Client(server_url=args.server,
                                bucket=preview_bucket,
                                collection=preview_collection)

    # 1. upload data
    print('Author uploads 20 random records')
    records = upload_records(client, 20)

    # 2. ask for a signature
    # 2.1 ask for review (noop on old versions)
    print('Editor asks for review')
    data = {"status": "to-review"}
    editor_client.patch_collection(data=data)
    # 2.2 check the preview collection (if enabled)
    if preview_client:
        print('Check preview collection')
        preview_records = preview_client.get_records()
        expected = existing + 20
        assert len(preview_records) == expected, '%s != %s records' % (len(preview_records), expected)
        metadata = preview_client.get_collection()['data']
        preview_signature = metadata.get('signature')
        assert preview_signature, 'Preview collection not signed'
        preview_timestamp = preview_client.get_records_timestamp()
    # 2.3 approve the review
    print('Reviewer approves and triggers signature')
    data = {"status": "to-sign"}
    reviewer_client.patch_collection(data=data)

    # 3. upload more data
    print('Author creates 20 others records')
    upload_records(client, 20)

    print('Editor updates 5 random records')
    for toupdate in random.sample(records, 5):
        editor_client.patch_record(data=dict(newkey=_rand(10), **toupdate))

    print('Author deletes 5 random records')
    for todelete in random.sample(records, 5):
        client.delete_record(id=todelete['id'])

    expected = existing + 20 + 20 - 5

    # 4. ask again for a signature
    # 2.1 ask for review (noop on old versions)
    print('Editor asks for review')
    data = {"status": "to-review"}
    editor_client.patch_collection(data=data)
    # 2.2 check the preview collection (if enabled)
    if preview_client:
        print('Check preview collection')
        preview_records = preview_client.get_records()
        assert len(preview_records) == expected, '%s != %s records' % (len(preview_records), expected)
        # Diff size is 20 + 5 if updated records are also all deleted,
        # or 30 if deletions and updates apply to different records.
        diff_since_last = preview_client.get_records(_since=preview_timestamp)
        assert 25 <= len(diff_since_last) <= 30, 'Changes since last signature are not consistent'

        metadata = preview_client.get_collection()['data']
        assert preview_signature != metadata['signature'], 'Preview collection not updated'

    # 2.3 approve the review
    print('Reviewer approves and triggers signature')
    data = {"status": "to-sign"}
    reviewer_client.patch_collection(data=data)

    # 5. wait for the result

    # 6. obtain the destination records and serialize canonically.

    records = list(dest_client.get_records())
    assert len(records) == expected, '%s != %s records' % (len(records), expected)
    timestamp = dest_client.get_records_timestamp()
    serialized = canonical_json(records, timestamp)
    print('Hash is %r' % compute_hash(serialized))

    # 7. get back the signed hash

    signature = dest_client.get_collection()['data']['signature']

    with open('pub', 'w') as f:
        f.write(signature['public_key'])

    # 8. verify the signature matches the hash
    signer = ECDSASigner(public_key='pub')
    try:
        signer.verify(serialized, signature)
        print('Signature OK')
    except Exception:
        print('Signature KO')
        raise
Exemplo n.º 30
0
class RecordTest(unittest.TestCase):
    def setUp(self):
        self.session = mock.MagicMock()
        self.client = Client(
            session=self.session, bucket='mybucket',
            collection='mycollection')

    def test_record_id_is_given_after_creation(self):
        mock_response(self.session, data={'id': 5678})
        record = self.client.create_record({'foo': 'bar'})
        assert 'id' in record['data'].keys()

    def test_generated_record_id_is_an_uuid(self):
        mock_response(self.session)
        self.client.create_record({'foo': 'bar'})
        id = self.session.request.mock_calls[0][1][1].split('/')[-1]

        uuid_regexp = r'[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}'
        self.assertRegexpMatches(id, uuid_regexp)

    def test_records_handles_permissions(self):
        mock_response(self.session)
        self.client.create_record(
            {'id': '1234', 'foo': 'bar'},
            permissions=mock.sentinel.permissions)
        self.session.request.assert_called_with(
            'put',
            '/buckets/mybucket/collections/mycollection/records/1234',
            data={'foo': 'bar', 'id': '1234'},
            permissions=mock.sentinel.permissions,
            headers=DO_NOT_OVERWRITE)

    def test_collection_argument_takes_precedence(self):
        mock_response(self.session)
        # Specify a different collection name for the client and the operation.
        client = Client(session=self.session, bucket='mybucket',
                        collection='wrong_collection')
        client.update_record(data={'id': '1234'}, collection='good_collection',
                             permissions=mock.sentinel.permissions)

        self.session.request.assert_called_with(
            'put',
            '/buckets/mybucket/collections/good_collection/records/1234',
            data={'id': '1234'},
            headers=None,
            permissions=mock.sentinel.permissions)

    def test_record_id_is_derived_from_data_if_present(self):
        mock_response(self.session)
        self.client.create_record(data={'id': '1234', 'foo': 'bar'},
                                  permissions=mock.sentinel.permissions)

        self.session.request.assert_called_with(
            'put',
            '/buckets/mybucket/collections/mycollection/records/1234',
            data={'id': '1234', 'foo': 'bar'},
            permissions=mock.sentinel.permissions,
            headers=DO_NOT_OVERWRITE)

    def test_data_and_permissions_are_added_on_create(self):
        mock_response(self.session)
        data = {'foo': 'bar'}
        permissions = {'read': ['mle']}

        self.client.create_record(
            id='1234',
            data=data,
            permissions=permissions)

        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with(
            'put', url, data=data, permissions=permissions,
            headers=DO_NOT_OVERWRITE)

    def test_creation_sends_if_none_match_by_default(self):
        mock_response(self.session)
        data = {'foo': 'bar'}

        self.client.create_record(
            id='1234',
            data=data)

        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with(
            'put', url, data=data, permissions=None, headers=DO_NOT_OVERWRITE)

    def test_creation_doesnt_add_if_none_match_when_overwrite(self):
        mock_response(self.session)
        data = {'foo': 'bar'}

        self.client.create_record(id='1234', data=data, safe=False)

        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with(
            'put', url, data=data, permissions=None, headers=None)

    def test_records_issues_a_request_on_delete(self):
        mock_response(self.session)
        self.client.delete_record('1234')
        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with('delete', url, headers=None)

    def test_record_issues_a_request_on_retrieval(self):
        mock_response(self.session, data={'foo': 'bar'})
        record = self.client.get_record('1234')

        self.assertEquals(record['data'], {'foo': 'bar'})
        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with('get', url)

    def test_collection_can_retrieve_all_records(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}])
        records = self.client.get_records()
        assert list(records) == [{'id': 'foo'}, {'id': 'bar'}]

    def test_collection_can_retrieve_records_timestamp(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"12345"'})
        timestamp = self.client.get_records_timestamp()
        assert timestamp == '12345'

    def test_records_timestamp_is_cached(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"12345"'})
        self.client.get_records()
        timestamp = self.client.get_records_timestamp()
        assert timestamp == '12345'
        assert self.session.request.call_count == 1

    def test_records_timestamp_is_cached_per_collection(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"12345"'})
        self.client.get_records(collection="foo")
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"67890"'})
        self.client.get_records(collection="bar")

        timestamp = self.client.get_records_timestamp("foo")
        assert timestamp == '12345'

        timestamp = self.client.get_records_timestamp("bar")
        assert timestamp == '67890'

    def test_pagination_is_followed(self):
        # Mock the calls to request.
        link = ('http://example.org/buckets/buck/collections/coll/records/'
                '?token=1234')

        self.session.request.side_effect = [
            # First one returns a list of items with a pagination token.
            build_response(
                [{'id': '1', 'value': 'item1'},
                 {'id': '2', 'value': 'item2'}, ],
                {'Next-Page': link}),
            # Second one returns a list of items without a pagination token.
            build_response(
                [{'id': '3', 'value': 'item3'},
                 {'id': '4', 'value': 'item4'}, ],
            ),
        ]
        records = self.client.get_records('bucket', 'collection')

        assert list(records) == [
            {'id': '1', 'value': 'item1'},
            {'id': '2', 'value': 'item2'},
            {'id': '3', 'value': 'item3'},
            {'id': '4', 'value': 'item4'},
        ]

    def test_pagination_supports_if_none_match(self):
        link = ('http://example.org/buckets/buck/collections/coll/records/'
                '?token=1234')

        self.session.request.side_effect = [
            # First one returns a list of items with a pagination token.
            build_response(
                [{'id': '1', 'value': 'item1'},
                 {'id': '2', 'value': 'item2'}, ],
                {'Next-Page': link}),
            # Second one returns a list of items without a pagination token.
            build_response(
                [{'id': '3', 'value': 'item3'},
                 {'id': '4', 'value': 'item4'}, ],
            ),
        ]
        self.client.get_records('bucket', 'collection',
                                if_none_match="1234")

        # Check that the If-None-Match header is present in the requests.
        self.session.request.assert_any_call(
            'get', '/buckets/collection/collections/bucket/records',
            headers={'If-None-Match': '"1234"'}, params={})
        self.session.request.assert_any_call(
            'get', link, headers={'If-None-Match': '"1234"'}, params={})

    def test_collection_can_delete_a_record(self):
        mock_response(self.session, data={'id': 1234})
        resp = self.client.delete_record(id=1234)
        assert resp == {'id': 1234}
        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with('delete', url, headers=None)

    def test_record_delete_if_match(self):
        data = {}
        mock_response(self.session, data=data)
        deleted = self.client.delete_record(
            collection='mycollection',
            bucket='mybucket',
            id='1',
            if_match=1234)
        assert deleted == data
        url = '/buckets/mybucket/collections/mycollection/records/1'
        self.session.request.assert_called_with(
            'delete', url, headers={'If-Match': '"1234"'})

    def test_record_delete_if_match_not_included_if_not_safe(self):
        data = {}
        mock_response(self.session, data=data)
        deleted = self.client.delete_record(
            collection='mycollection',
            bucket='mybucket',
            id='1',
            if_match=1234,
            safe=False)
        assert deleted == data
        url = '/buckets/mybucket/collections/mycollection/records/1'
        self.session.request.assert_called_with(
            'delete', url, headers=None)

    def test_update_record_gets_the_id_from_data_if_exists(self):
        mock_response(self.session)
        self.client.update_record(
            bucket='mybucket', collection='mycollection',
            data={'id': 1, 'foo': 'bar'})

        self.session.request.assert_called_with(
            'put', '/buckets/mybucket/collections/mycollection/records/1',
            data={'id': 1, 'foo': 'bar'}, headers=None, permissions=None)

    def test_update_record_handles_if_match(self):
        mock_response(self.session)
        self.client.update_record(
            bucket='mybucket', collection='mycollection',
            data={'id': 1, 'foo': 'bar'}, if_match=1234)

        headers = {'If-Match': '"1234"'}
        self.session.request.assert_called_with(
            'put', '/buckets/mybucket/collections/mycollection/records/1',
            data={'id': 1, 'foo': 'bar'}, headers=headers, permissions=None)

    def test_patch_record_uses_the_patch_method(self):
        mock_response(self.session)
        self.client.patch_record(
            bucket='mybucket', collection='mycollection',
            data={'id': 1, 'foo': 'bar'})

        self.session.request.assert_called_with(
            'patch', '/buckets/mybucket/collections/mycollection/records/1',
            data={'id': 1, 'foo': 'bar'}, headers=None, permissions=None)

    def test_update_record_raises_if_no_id_is_given(self):
        with self.assertRaises(KeyError) as cm:
            self.client.update_record(
                data={'foo': 'bar'},  # Omit the id on purpose here.
                bucket='mybucket',
                collection='mycollection'
            )
        assert text_type(cm.exception) == (
            "'Unable to update a record, need an id.'")

    def test_get_or_create_doesnt_raise_in_case_of_conflict(self):
        data = {
            'permissions': mock.sentinel.permissions,
            'data': {'foo': 'bar'}
        }
        self.session.request.side_effect = [
            get_http_error(status=412),
            (data, None)
        ]
        returned_data = self.client.create_record(
            bucket="buck",
            collection="coll",
            data={'id': 1234,
                  'foo': 'bar'},
            if_not_exists=True)  # Should not raise.
        assert returned_data == data

    def test_get_or_create_raise_in_other_cases(self):
        self.session.request.side_effect = get_http_error(status=500)
        with self.assertRaises(KintoException):
            self.client.create_record(
                bucket="buck",
                collection="coll",
                data={'foo': 'bar'},
                if_not_exists=True)

    def test_create_record_raises_a_special_error_on_403(self):
        self.session.request.side_effect = get_http_error(status=403)
        with self.assertRaises(KintoException) as e:
            self.client.create_record(
                bucket="buck",
                collection="coll",
                data={'foo': 'bar'})
        expected_msg = ("Unauthorized. Please check that the collection exists"
                        " and that you have the permission to create or write "
                        "on this collection record.")
        assert e.exception.message == expected_msg
Exemplo n.º 31
0
class FunctionalTest(unittest2.TestCase):

    def __init__(self, *args, **kwargs):
        super(FunctionalTest, self).__init__(*args, **kwargs)
        # XXX Read the configuration from env variables.
        self.server_url = SERVER_URL
        self.auth = DEFAULT_AUTH

        # Read the configuration.
        self.config = configparser.RawConfigParser()
        self.config.read(os.path.join(__HERE__, 'config/kinto.ini'))
        self.client = Client(server_url=self.server_url, auth=self.auth)

    def tearDown(self):
        # Delete all the created objects
        flush_url = urljoin(self.server_url, '/__flush__')
        resp = requests.post(flush_url)
        resp.raise_for_status()

    def get_user_id(self, credentials):
        hmac_secret = self.config.get('app:main', 'kinto.userid_hmac_secret')
        credentials = '%s:%s' % credentials
        digest = kinto_core_utils.hmac_digest(hmac_secret, credentials)
        return 'basicauth:%s' % digest

    def test_bucket_creation(self):
        bucket = self.client.create_bucket('mozilla')
        user_id = self.get_user_id(self.auth)
        assert user_id in bucket['permissions']['write']

    def test_bucket_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        # Should not raise.
        self.client.create_bucket('mozilla', if_not_exists=True)

    def test_buckets_retrieval(self):
        self.client.create_bucket('mozilla')
        buckets = self.client.get_buckets()
        assert len(buckets) == 1

    def test_bucket_retrieval(self):
        self.client.create_bucket('mozilla')
        self.client.get_bucket('mozilla')
        # XXX Add permissions handling during creation and check they are
        # present during retrieval.

    def test_bucket_modification(self):
        bucket = self.client.create_bucket('mozilla', data={'version': 1})
        assert bucket['data']['version'] == 1
        bucket = self.client.patch_bucket('mozilla', data={'author': 'you'})
        assert bucket['data']['version'] == 1
        assert bucket['data']['author'] == 'you'
        bucket = self.client.update_bucket('mozilla', data={'date': 'today'})
        assert bucket['data']['date'] == 'today'
        assert 'version' not in bucket['data']

    def test_bucket_retrieval_fails_when_not_created(self):
        self.assertRaises(BucketNotFound, self.client.get_bucket,
                          'non-existent')

    def test_bucket_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.delete_bucket('mozilla')
        self.assertRaises(BucketNotFound, self.client.get_bucket, 'mozilla')

    def test_bucket_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.delete_bucket('mozilla')
        self.client.delete_bucket('mozilla', if_exists=True)

    def test_buckets_deletion(self):
        self.client.create_bucket('mozilla')
        buckets = self.client.delete_buckets()
        assert buckets[0]['id'] == 'mozilla'
        self.assertRaises(BucketNotFound, self.client.get_bucket, 'mozilla')

    def test_buckets_deletion_when_no_buckets_exist(self):
        deleted_buckets = self.client.delete_buckets()
        assert len(deleted_buckets) == 0

    def test_bucket_save(self):
        self.client.create_bucket('mozilla', permissions={'write': ['alexis']})
        bucket = self.client.get_bucket('mozilla')
        assert 'alexis' in bucket['permissions']['write']

    def test_group_creation(self):
        self.client.create_bucket('mozilla')
        self.client.create_group(
            'payments', bucket='mozilla',
            data={'members': ['blah', ]},
            permissions={'write': ['blah', ]})
        # Test retrieval of a group gets the permissions as well.
        group = self.client.get_group('payments', bucket='mozilla')
        assert 'blah' in group['permissions']['write']

    def test_group_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group(
            'payments', bucket='mozilla',
            data={'members': ['blah', ]},
            permissions={'write': ['blah', ]},
            if_not_exists=True)

    def test_group_creation_if_bucket_does_not_exist(self):
        with pytest.raises(KintoException):
            self.client.create_group(
                'payments', bucket='mozilla',
                data={'members': ['blah', ]})
            self.client.create_group(
                'payments', bucket='mozilla',
                data={'members': ['blah', ]},
                if_not_exists=True)

    def test_group_update(self):
        self.client.create_bucket('mozilla')
        group = self.client.create_group(
                    'payments', bucket='mozilla',
                    data={'members': ['blah', ]},
                    if_not_exists=True)
        assert group['data']['members'][0] == 'blah'
        group = self.client.update_group(
                    data={'members': ['blah', 'foo']},
                    group='payments', bucket='mozilla')
        self.assertEquals(group['data']['members'][1], 'foo')

    def test_group_list(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('receipts', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group('assets', bucket='mozilla', data={'members': ['blah', ]})
        # The returned groups should be strings.
        groups = self.client.get_groups('mozilla')
        self.assertEquals(2, len(groups))
        self.assertEquals(set([coll['id'] for coll in groups]),
                          set(['receipts', 'assets']))

    def test_group_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_group('payments', bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_group_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_group('payments', bucket='mozilla')
        self.client.delete_group('payments', bucket='mozilla', if_exists=True)

    def test_group_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session, 'request', side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_group('payments', bucket='mozilla', if_exists=True)

    def test_groups_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('amo', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group('blocklist', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_groups(bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_groups_deletion_when_no_groups_exist(self):
        self.client.create_bucket('mozilla')
        deleted_groups = self.client.delete_groups(bucket='mozilla')
        assert len(deleted_groups) == 0

    def test_collection_creation(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection(
            'payments', bucket='mozilla',
            permissions={'write': ['alexis', ]}
        )

        # Test retrieval of a collection gets the permissions as well.
        collection = self.client.get_collection('payments', bucket='mozilla')
        assert 'alexis' in collection['permissions']['write']

    def test_collection_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        # Should not raise.
        self.client.create_collection('payments', bucket='mozilla',
                                      if_not_exists=True)

    def test_collection_list(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('receipts', bucket='mozilla')
        self.client.create_collection('assets', bucket='mozilla')

        # The returned collections should be strings.
        collections = self.client.get_collections('mozilla')
        self.assertEquals(2, len(collections))

        self.assertEquals(set([coll['id'] for coll in collections]),
                          set(['receipts', 'assets']))

    def test_collection_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments', bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collection_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments', bucket='mozilla', if_exists=True)

    def test_collection_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session, 'request', side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_collection('payments', bucket='mozilla', if_exists=True)

    def test_collections_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('amo', bucket='mozilla')
        self.client.create_collection('blocklist', bucket='mozilla')
        self.client.delete_collections(bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collections_deletion_when_no_collections_exist(self):
        self.client.create_bucket('mozilla')
        deleted_collections = self.client.delete_collections(bucket='mozilla')
        assert len(deleted_collections) == 0

    def test_record_creation_and_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})
        record = client.get_record(created['data']['id'])
        assert 'alexis' in record['permissions']['read']

    def test_records_list_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record(data={'foo': 'bar'},
                             permissions={'read': ['alexis']})
        records = client.get_records()
        assert len(records) == 1

    def test_records_paginated_list_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        for i in range(10):
            client.create_record(data={'foo': 'bar'},
                                 permissions={'read': ['alexis']})
        # Kinto is running with kinto.paginate_by = 5
        records = client.get_records()
        assert len(records) == 10

    def test_single_record_save(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})
        created['data']['bar'] = 'baz'

        # XXX enhance this in order to have to pass only one argument, created.
        client.update_record(id=created['data']['id'], data=created['data'])

        retrieved = client.get_record(created['data']['id'])
        assert 'alexis' in retrieved['permissions']['read']
        assert retrieved['data']['foo'] == u'bar'
        assert retrieved['data']['bar'] == u'baz'
        assert created['data']['id'] == retrieved['data']['id']

    def test_single_record_doesnt_overwrite(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})

        with self.assertRaises(KintoException):
            # Create a second record with the ID of the first one.
            client.create_record(data={'id': created['data']['id'],
                                       'bar': 'baz'})

    def test_single_record_creation_if_not_exists(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'})
        client.create_record(data={'id': created['data']['id'],
                                   'bar': 'baz'},
                             if_not_exists=True)

    def test_single_record_can_overwrite(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})

        client.create_record(data={'id': created['data']['id'],
                                   'bar': 'baz'}, safe=False)

    def test_one_record_deletion(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record({'foo': 'bar'})
        deleted = client.delete_record(record['data']['id'])
        assert deleted['deleted'] is True
        assert len(client.get_records()) == 0

    def test_record_deletion_if_exists(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record({'foo': 'bar'})
        deleted = client.delete_record(record['data']['id'])
        deleted_if_exists = client.delete_record(record['data']['id'], if_exists=True)
        assert deleted['deleted'] is True
        assert deleted_if_exists is None

    def test_multiple_record_deletion(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record({'foo': 'bar'})
        client.delete_records()
        assert len(client.get_records()) == 0

    def test_records_deletion_when_no_records_exist(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        deleted_records = client.delete_records()
        assert len(deleted_records) == 0

    def test_bucket_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a bucket and share it with alice.
        self.client.create_bucket('shared-bucket',
                                  permissions={'read': [alice_userid, ]})

        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_bucket('shared-bucket')

    def test_updating_data_on_a_group(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla')
        client.create_bucket()
        client.create_group('payments', data={'members': []})
        client.patch_group('payments', data={'secret': 'psssssst!'})
        group = client.get_group('payments')
        assert group['data']['secret'] == 'psssssst!'

    def test_updating_data_on_a_collection(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()

        client.patch_collection(data={'secret': 'psssssst!'})
        collection = client.get_collection()
        assert collection['data']['secret'] == 'psssssst!'

    def test_collection_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        self.client.create_bucket('bob-bucket')
        self.client.create_collection(
            'shared',
            bucket='bob-bucket',
            permissions={'read': [alice_userid, ]})

        # Try to read the collection as Alice.
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_collection('shared', bucket='bob-bucket')

    def test_record_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a record, and share it with Alice.
        self.client.create_bucket('bob-bucket')
        self.client.create_collection('bob-personal-collection',
                                      bucket='bob-bucket')
        record = self.client.create_record(
            data={'foo': 'bar'},
            permissions={'read': [alice_userid, ]},
            bucket='bob-bucket',
            collection='bob-personal-collection')

        # Try to read the record as Alice
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        record = alice_client.get_record(
            id=record['data']['id'],
            bucket='bob-bucket',
            collection='bob-personal-collection')

        assert record['data']['foo'] == 'bar'

    def test_request_batching(self):
        with self.client.batch(bucket='mozilla', collection='fonts') as batch:
            batch.create_bucket()
            batch.create_collection()
            batch.create_record(data={'foo': 'bar'},
                                permissions={'read': ['natim']})
            batch.create_record(data={'bar': 'baz'},
                                permissions={'read': ['alexis']})

        records = self.client.get_records(bucket='mozilla', collection='fonts')
        assert len(records) == 2

    def test_replication(self):
        # First, create a few records on the first kinto collection.
        with self.client.batch(bucket='origin', collection='coll') as batch:
            batch.create_bucket()
            batch.create_collection()

            for n in range(10):
                batch.create_record(data={'foo': 'bar', 'n': n})

        origin = Client(
            server_url=self.server_url,
            auth=self.auth,
            bucket='origin',
            collection='coll'
        )
        destination = Client(
            server_url=self.server_url,
            auth=self.auth,
            bucket='destination',
            collection='coll')

        replication.replicate(origin, destination)
        records = self.client.get_records(bucket='destination',
                                          collection='coll')
        assert len(records) == 10
Exemplo n.º 32
0
from kinto_http import Client

source = Client(server_url="https://kinto.notmyidea.org/v1")
destination = Client(server_url="https://kinto.notmyidea.org/v1",
                     auth=('user', 'pass'),
                     bucket='webnotesapp')

records = source.get_records(bucket='ametaireau', collection='notes')
print("got %s records" % len(records))
destination.create_collection(id='ametaireau',
                              permissions={'read': ['system.Everyone']})

for record in records:
    destination.create_record(data=record, collection='ametaireau')
Exemplo n.º 33
0
class AppWindow(QtWidgets.QMainWindow,Ui_MainWindow):
    def __init__(self, parent = None):
        QtWidgets.QMainWindow.__init__(self, parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setupUi(self)




######################################################################################
######################################################################################
        self.settings = Settings()
        self.initRep()
        self.initKintoClient()
        self.syncRecipes()
        self.dlgEditG = Dialog(self)
        self.dlgEditH = DialogH(self)
        self.dlgEditD = DialogD(self)
        self.dlgEditY = DialogL(self)
        self.dlgPref = DialogPref(self)
        self.dlgStep = DialogStep(self)
        self.dlgMash = DialogMash(self)



        self.base = ImportBase()
        self.mashProfileExport = ExportMash()


#        self.base.importBeerXML()
        self.s=0
        self.recipe = None


        #Les connexions
        self.actionEnregistrer.triggered.connect(self.enregistrer)
        self.actionQuitter.triggered.connect(app.quit)
#        self.connect(self.actionQuitter, QtCore.SIGNAL("triggered()"), app, QtCore.SLOT("quit()"))

        self.actionShowJournal.triggered.connect(self.showJournal)


        self.actionEditGrains.triggered.connect(self.editGrains)
        self.actionEditHoublons.triggered.connect(self.editHoublons)
        self.actionEditDivers.triggered.connect(self.editDivers)
        self.actionEditLevures.triggered.connect(self.editLevures)
        self.actionRestaurerIngredients.triggered.connect(self.restoreDataBase)
        self.actionImportIng.triggered.connect(self.importIng)
        self.actionManageProfiles.triggered.connect(self.seeMash)

        self.actionAbout.triggered.connect(self.about)

        self.actionAllTools.triggered.connect(self.showTools)

        self.actionPreferences.triggered.connect(self.dialogPreferences)



        #######################################################################################################
        # Profil de brassage       #########################################################################################################


        self.listWidgetSteps.itemSelectionChanged.connect (self.stepDetails)
        self.listWidgetMashProfiles.itemSelectionChanged.connect (self.mashClicked)
        self.buttonBoxMashDetails.rejected.connect(self.mashRejected)
#        self.comboBoxStepType.addItems(["Infusion", "Température", "Décoction"])
        self.pushButtonStepEdit.clicked.connect(self.stepEdit)
        self.dlgStep.stepChanged.connect(self.stepReload)
        self.pushButtonStepRemove.clicked.connect(self.removeStep)
        self.pushButtonNewStep.clicked.connect(self.addStep)
        self.pushButtonMashEdit.clicked.connect(self.mashEdit)
        self.dlgMash.mashChanged.connect(self.mashReload)
        self.pushButtonNewProfile.clicked.connect(self.addMash)
        self.pushButtonRemoveProfile.clicked.connect(self.removeMash)
        self.pushButtonSaveProfile.clicked.connect(self.saveProfile)

        #La bibliotheque
        ###################################################################################################################
        ###################################################################################################################


        # self.listdir(recettes_dir)
        self.showLib()

###################################################################################################
######## gestion des arguments au lancement du programme  #########################################


        argumentsList=QtWidgets.QApplication.arguments()
        if len(argumentsList) > 1 :
            logger.debug("la liste d'arguments: %s",argumentsList)
            logger.debug("le chemin: %s",argumentsList[1])
            # for part in argumentsList :
            #     recipePath=recipePath + " " + part
            try:
                recipePath= argumentsList[1]
                for part in argumentsList[2:] :
                    recipePath= recipePath +" " + part

                self.openRecipeFile(recipePath)
            except :
                pass
        else:
            pass

########################################################################################################################
####################################################################################################################
# le signal émit à la fermeture de la fenêtre de préférences
        self.dlgPref.prefAccepted.connect(self.prefReload)



###########################################################
############### Journal ##############################
######################################################

    def loadJournal(self):
        self.journal=Journal()
        self.journal.loadJournal()
        # self.actionEditJournal.setEnabled(True)

    @QtCore.pyqtSlot()
    def showJournal(self,entry=" '' ") :
        self.stackedWidget.setCurrentIndex(0)
        self.loadJournal()
        pyDir = os.path.abspath(os.path.dirname(__file__))
        baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/"))
        self.webViewBiblio.setHtml(self.journal.export("html",entry), baseUrl)
        self.webViewBiblio.page().mainFrame().addToJavaScriptWindowObject("main", self)
        # self.webViewBiblio.page().settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
        # self.webInspector = QtWebKit.QWebInspector(self)
        # self.webInspector.setPage(self.webViewBiblio.page())
        # self.webInspector.setVisible(True)
        # self.verticalLayout_13.addWidget(self.webInspector)


    @QtCore.pyqtSlot(str, str)
    def addToJournal(self,event, recipeName) :
        self.loadJournal()
        entry = '''{recipe:%s,date:%s,event:%s,editing:'True'} ''' %( "'" + recipeName + "'", "'" + str(int(time.time())) + "'" , "'" + self.journal.eventsLabels[event] + "'")
        self.showJournal(entry)


    @QtCore.pyqtSlot(str)
    def dumpJournal(self,journalJson) :
        journalJson= '{"name":"journal","items": %s }' %journalJson
        d=json.loads(journalJson)
        with open(journal_file, mode="w", encoding="utf-8") as f :
            json.dump(d,f,indent=2)



############## Bibliothèque ##############################
##########################################################
    @QtCore.pyqtSlot()
    def showLib(self) :
        # data = json.dumps(self.recipesSummary)
        # data = data.replace("'","&#39;")
        self.stackedWidget.setCurrentIndex(0)
        self.brewdayLock = 0

        self.webSettings = self.webViewBiblio.settings()
        self.webSettings.setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True)

        pyDir = os.path.abspath(os.path.dirname(__file__))
        baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/html/"))
        self.webViewBiblio.setHtml(LibExporterRepository['html'](), baseUrl)
        self.webViewBiblio.page().mainFrame().addToJavaScriptWindowObject("main", self)
        self.webViewBiblio.page().settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
        self.webViewBiblio.page().action(QtWebKitWidgets.QWebPage.Reload).setVisible(False)


    @QtCore.pyqtSlot(str)
    def deleteLib(self,path) :
        confirmation = QtWidgets.QMessageBox.question(self,
                            self.tr("Supprimer"),
                            self.tr("La recette sera définitivement supprimée <br/> Continuer ?"),
                            QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
        if (confirmation == QtWidgets.QMessageBox.Yes):
            os.remove(path)
            self.listdir(recettes_dir)
            self.showLib()
        else :
            self.showLib()


    @QtCore.pyqtSlot()
    def backWebViewBiblio(self) :
        self.stackedWidget.setCurrentIndex(0)


    @QtCore.pyqtSlot(str, str)
    def saveRecipe(self, recipe, path) :
        logger.debug(path)
        recipeFile = QtCore.QFile(path)
        if recipeFile.open(QtCore.QIODevice.WriteOnly):
            try:
                stream = QtCore.QTextStream(recipeFile)
                stream.setCodec("UTF-8")
                stream << recipe
            finally:
                recipeFile.close()
        else:
            # TODO : Prévenir l'utilisateur en cas d'échec de l'enregistrement
            pass

    @QtCore.pyqtSlot(result=str)
    def createPath(self, file_id=None) :
        if file_id is None:
            file_id = str(int(time.time()*10))
        path = recettes_dir + "/" + file_id + ".xml"
        logger.debug(path)
        return path

    @QtCore.pyqtSlot()
    def resetLock(self):
        self.brewdayLock = 0;




############# Mode Brassage ################################
############################################################

    @QtCore.pyqtSlot(str)
    def showBrewdayMode(self, data):
        if self.brewdayLock == 0 :
            self.stackedWidget.setCurrentIndex(1)
            self.brewdayLock = 1
            data = data.replace("'","&#39;")
            pyDir = os.path.abspath(os.path.dirname(__file__))
            baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/"))
            self.webViewBrewday.setHtml(BrewdayExporterRepository['html'](data), baseUrl)
            self.webViewBrewday.page().mainFrame().addToJavaScriptWindowObject("main", self)
            self.webViewBrewday.page().settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
            self.webViewBrewday.page().action(QtWebKitWidgets.QWebPage.Reload).setVisible(False)
        else :
            self.stackedWidget.setCurrentIndex(1)





###### Outils ############################################
##########################################################

    @QtCore.pyqtSlot()
    def showTools(self):
        self.stackedWidget.setCurrentIndex(0)
        pyDir = os.path.abspath(os.path.dirname(__file__))
        baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/"))
        self.webViewBiblio.setHtml(ToolExporterRepository["html"](), baseUrl)
        self.webViewBiblio.page().mainFrame().addToJavaScriptWindowObject("main", self)
        # self.webViewBiblio.page().settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
        # self.webInspector = QtWebKit.QWebInspector(self)
        # self.webInspector.setPage(self.webViewBiblio.page())
        # self.webInspector.setVisible(True)
        # self.verticalLayout_13.addWidget(self.webInspector)



    @QtCore.pyqtSlot(result=str)
    def dataRecipes(self) :
        # f = open(recipeData_file, 'w')
        # f.write(self.recipesSummary)
        self.listdir(recettes_dir)
        return self.recipesSummary

    @QtCore.pyqtSlot(result=str)
    def dataProfiles(self) :
        return self.mashProfileExport.exportJson(ImportBase().listeMashes)


    @QtCore.pyqtSlot(result=str)
    def dataIngredients(self) :
        return ImportBase().exportjson()


    @QtCore.pyqtSlot(result=str)
    def dataPref(self) :
        dic = {}
        dic["boilOffRate"] = settings.conf.value("BoilOffRate")
        dic["coolingLossRate"] = settings.conf.value("CoolingLoss")
        dic["grainTemp"] = settings.conf.value("GrainTemp")
        dic["fudgeFactor"] = settings.conf.value("FudgeFactor")
        dic["grainRetention"] = settings.conf.value("GrainRetention")
        dic = json.dumps(dic)
        return dic











    #Une fonction qui gère l'aperçu des couleurs.
    #Contient un tupple avec plusieurs références de couleurs, classées par rang selon la valeur SRM.
    #################################################################################################
    # def colorPreview (self) :
    #     self.colorTuppleSrm = ('FFE699', 'FFD878', 'FFCA5A', 'FFBF42', 'FBB123', 'F8A600', 'F39C00', 'EA8F00', 'E58500', 'DE7C00', 'D77200', 'CF6900', 'CB6200', 'C35900','BB5100', 'B54C00', 'B04500', 'A63E00', 'A13700', '9B3200', '952D00', '8E2900', '882300', '821E00', '7B1A00', '771900', '701400', '6A0E00', '660D00','5E0B00','5A0A02','600903', '520907', '4C0505', '470606', '440607', '3F0708', '3B0607', '3A070B', '36080A')

    #     colorRef= round(self.recipe.compute_EBC()/1.97)

    #     if colorRef >= 30 :
    #         color = "#" + self.colorTuppleSrm[30]
    #     elif colorRef <= 1 :
    #         color = "#" + self.colorTuppleSrm[0]
    #     else :
    #         color = "#" + self.colorTuppleSrm[colorRef-1]
    #     self.widgetColor.setStyleSheet("background-color :" + color)


    def liste_fichiers_recettes(self, rootdir):
        for root, subFolders, files in os.walk(rootdir):
            for file2 in files:
                yield (file2, os.path.join(root,file2))

    def listdir(self, rootdir) :
        summaries=[]
        for filename, recipe in self.liste_fichiers_recettes(rootdir):
            try :
                summaries.append(self.jsonRecipeLib(recipe))
            except :
                logger.debug("le fichier %s n'est pas une recette" %(recipe))
        self.recipesSummary = "[" + ",".join(summaries) + "]"
        logger.debug("%s recettes détectées" %(len(summaries)))



    def jsonRecipeLib(self,recipe) :
        self.s = recipe
        self.recipe = Recipe.parse(recipe)
        data = self.recipe.export("json")
        data = data[1:-1]
        return data



    def initRep(self) :
        home = QtCore.QDir(home_dir)
        config = QtCore.QDir(config_dir)
        logger.debug (config)
        if not config.exists() :
            home.mkpath (config_dir)
        else :
            pass
        database = QtCore.QFile(database_file)
        if not database.exists() :
            database.copy(database_root, database_file)
        else :
            pass
        recettes = QtCore.QFile(recettes_dir)
        if not recettes.exists() :
            try :
                shutil.copytree(samples_dir, samples_target)
            except :
                home.mkpath(recettes_dir)
        mash  = QtCore.QFile(mash_file)
        if not mash.exists() :
            mash.copy(mash_root, mash_file)
        else :
            pass
        journal  = QtCore.QFile(journal_file)
        if not journal.exists() :
            journal.copy(journal_root, journal_file)
        else :
            pass

        # on configure des valeurs par défaut
        if not settings.conf.contains("BoilOffRate") :
            settings.conf.setValue("BoilOffRate", 10)
        if not settings.conf.contains("CoolingLoss") :
            settings.conf.setValue("CoolingLoss", 5)
        if not settings.conf.contains("GrainTemp") :
            settings.conf.setValue("GrainTemp", 20)
        if not settings.conf.contains("FudgeFactor") :
            settings.conf.setValue("FudgeFactor", 1.7)
        if not settings.conf.contains("GrainRetention") :
            settings.conf.setValue("GrainRetention", 1)
        if not settings.conf.contains("Menus") :
            settings.conf.setValue("Menus", "button")


    def prefReload(self) :
        if platform == 'win32':
            recettes_dir = settings.conf.value("pathWin32")
        else :
            recettes_dir = settings.conf.value("pathUnix")
        self.initRep()
        self.initKintoClient()
        self.listdir(recettes_dir)
        self.showLib()

    def initKintoClient(self):
        self.kinto_client = None
        kinto_url = settings.conf.value("KintoServerUrl")
        if kinto_url is not None and kinto_url != "":
            kinto_bucket = settings.conf.value("KintoDefaultBucket")
            kinto_credentials=(settings.conf.value("KintoBasicUserCred"), settings.conf.value("KintoBasicPasswordCred"))
            logger.debug(kinto_credentials)
            if kinto_bucket is None or kinto_bucket == "":
                kinto_bucket = "joliebulle"
                logger.debug("using default bucket 'joliebulle'")
            try:
                tmp_client = Client(server_url=kinto_url, auth = kinto_credentials)
                tmp_client.create_bucket(id=kinto_bucket, if_not_exists=True)
                self.kinto_client = Client(server_url=kinto_url, bucket=kinto_bucket, auth = kinto_credentials)

                # Création des collections
                self.kinto_client.create_collection(id='recipes', if_not_exists=True)
                self.kinto_client.create_collection(id='ingredients', if_not_exists=True)
                
                logger.info("Synchronize with kinto server at :" + repr(self.kinto_client))
            except Exception as e:
                logger.warn("Failed to initialize Kinto synchronisation: " + repr(e))

    def syncRecipes(self):
        recipes_collection = 'recipes'
        if self.kinto_client is None:
            return

        # Sync local recipes
        id_recettes_locales=[]
        for filename, full_filename in self.liste_fichiers_recettes(recettes_dir):
            recipe_id = filename.replace('.xml', '')
            id_recettes_locales.append(recipe_id)
            try:
                remote_recipe = self.kinto_client.get_record(id=recipe_id, collection=recipes_collection)
                logger.debug("recipe " + recipe_id + " already exists on server, update if needed")
                remote_timestamp = int(remote_recipe['data']['last_modified']/1000)
                local_timestamp = int(os.path.getmtime(full_filename))
                logger.debug(str(remote_timestamp) + " " + str(local_timestamp))
                if remote_timestamp < local_timestamp:
                    logger.info("Mise à jour de la recette distante " + recipe_id)
                    # La recette distant doit être mise à jour
                    local_recipe = Recipe.parse(full_filename)
                    data = local_recipe.export("dict")
                    ret = self.kinto_client.update_record(id=recipe_id, collection=recipes_collection, data=data)
                    new_timestamp = ret['data']['last_modified']
                    #MAJ du mtime du fichier pour marquer la synchronisation
                    os.utime(full_filename, times=(int(new_timestamp/1000), int(new_timestamp/1000)))
                if remote_timestamp > local_timestamp:
                    # La recette distante doit être mise à jour
                    logger.info("Mise à jour de la recette locale " + recipe_id)
                    new_recipe = Recipe.parse(remote_recipe['data'], "dict")
                    self.doEnregistrerRecette(new_recipe, full_filename)
                    os.utime(full_filename, times=(int(remote_timestamp), int(remote_timestamp)))
            except KintoException:
                logger.debug("recipe " + recipe_id + " doesn't exists on server, sending it")
                new_recipe = Recipe.parse(full_filename)
                data = new_recipe.export("dict")
                ret = self.kinto_client.create_record(id=recipe_id, collection=recipes_collection, data=data)
                new_timestamp = ret['data']['last_modified']
                #MAJ du mtime du fichier pour marquer la synchronisation
                os.utime(full_filename, times=(int(new_timestamp/1000), int(new_timestamp/1000)))

        recipes = self.kinto_client.get_records(collection='recipes')
        for recipe in recipes:
            if recipe['id'] not in id_recettes_locales:
                #Recette distante non présente localement
                new_recipe = Recipe.parse(recipe, "dict")
                fullname = recipe['id'] + ".xml"
                logger.debug("Création de la recette " + fullname)
                self.doEnregistrerRecette(new_recipe, os.path.join(recettes_dir, fullname))
                os.utime(full_filename, times=(int(remote_timestamp), int(remote_timestamp)))


    @QtCore.pyqtSlot()
    def switchToLibrary(self) :
        self.stackedWidget.setCurrentIndex(0)
        # self.viewRecipeLib(self.s)

    def switchToMash(self) :
        self.stackedWidget.setCurrentIndex(2)


    def restoreDataBase(self) :
        home = QtCore.QDir(home_dir)
        config = QtCore.QDir(config_dir)
        database = QtCore.QFile(database_file)
        confirmation = QtWidgets.QMessageBox.question(self,
                                    self.tr("Remplacer la base ?"),
                                    self.tr("La base des ingrédients actuelle va être effacée et remplacée par la base originale. Toutes vos modifications vont être effacées. Un redémarrage de l'application sera nécessaire.<br> Continuer ?"),
                                    QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
        if (confirmation == QtWidgets.QMessageBox.Yes):
            database.remove(database_file)
            database.copy(database_root, database_file)
        else :

            pass


    def editGrains(self) :
        self.dlgEditG.setModal(True)
        self.dlgEditG.setModel()
        self.dlgEditG.show()

    def editHoublons(self) :
        self.dlgEditH.setModal(True)
        self.dlgEditH.setModel()
        self.dlgEditH.show()

    def editDivers(self) :
        self.dlgEditD.setModal(True)
        self.dlgEditD.setModel()
        self.dlgEditD.show()

    def editLevures(self) :
        self.dlgEditY.setModal(True)
        self.dlgEditY.setModel()
        self.dlgEditY.show()

    @QtCore.pyqtSlot(float, float, float, float)
    def preBoilCheck(self,volPreBoil,preBoilSg,GU,volume) :
        self.dlgPreBoil = DialogPreBoil(self)
        self.dlgPreBoil.setData(volPreBoil,preBoilSg,GU,volume)
        self.dlgPreBoil.setModal(True)
        self.dlgPreBoil.show()


    def dialogPreferences (self) :
        self.dlgPref.setModal(True)
        self.dlgPref.show()




    def importBeerXML(self) :
        fichierBeerXML = self.s
        try:
            self.recipe = Recipe.parse(fichierBeerXML)
            self.currentRecipeMash = self.recipe.mash

        except :
            errors = Errors()
            errors.warningXml()


    def about(self) :
        about = DialogAbout(self)
        about.show()


    def doEnregistrerRecette(self, recipe, destination):
        recipeFile = QtCore.QFile(destination)
        if recipeFile.open(QtCore.QIODevice.WriteOnly):
            try:
                stream = QtCore.QTextStream(recipeFile)
                stream.setCodec("UTF-8")
                stream << recipe.export("beerxml")
            finally:
                recipeFile.close()
        else:
            # TODO : Prévenir l'utilisateur en cas d'échec de l'enregistrement
            pass

    def enregistrerRecette(self, destination):
        self.doEnregistrerRecette(self.recipe, destination)
        self.fileSaved = True

    def enregistrer (self) :
        if self.recipe.name != self.lineEditRecette.text() :
            self.nameChanged = True
        else :
            self.nameChanged = False

        self.recipe.name = self.lineEditRecette.text()
        self.recipe.style = self.lineEditGenre.text()
        self.recipe.brewer = self.lineEditBrewer.text()
        self.recipe.boil = self.spinBoxBoil.value()
        if not self.s:
            destination = recettes_dir + "/" + self.recipe.name.replace('/', ' ') + ".xml"
            if os.path.exists(destination) :
                errors=Errors()
                errors.warningExistingPath()
                self.fileSaved = False
            else :
                self.s = destination
                self.enregistrerRecette(destination)
        else :
            self.enregistrerRecette(self.s)




    def enregistrerSous (self) :
        self.s = QtGui.QFileDialog.getSaveFileName (self,
                                                    self.tr("Enregistrer dans un fichier"),
                                                    recettes_dir + "/" + self.recipe.name.replace('/', ' ') + ".xml",
                                                    "BeerXML (*.xml)")
        self.enregistrerRecette(self.s)

    @QtCore.pyqtSlot(str)
    def copyBbcode (self, bbcode):
        app.clipboard().setText(bbcode)


    def importIng(self):
        s = QtWidgets.QFileDialog.getOpenFileName(self,
            self.tr("Ouvrir un fichier"),
            home_dir,
            )
        if not s :
            pass
        else :
            self.importIngList = ImportIng()
            self.importIngList.parseFile(s)


    def mashComboChanged (self) :
        #on remet le verrou à 0, il va falloir recalculer en repassant en brewday mode
        self.brewdayLock = 0
        try :
            i =self.comboBoxMashProfiles.currentIndex()
            self.currentMash = ImportBase().listeMashes[i]
        except :
            self.currentMash = self.currentRecipeMash
        if i == -1 :
            self.currentMash = Mash()
        self.recipe.mash = self.currentMash

    def seeMash(self) :
        self.switchToMash()
        index = self.listWidgetMashProfiles.currentRow()
        i = self.listWidgetSteps.currentRow()
        self.listWidgetMashProfiles.clear()
        self.listWidgetSteps.clear()

        self.numMash = len(ImportBase().listeMashes)
        #self.numSteps = self.mashProfilesBase.numSteps
        self.popMashList()
        self.pushButtonMashEdit.setEnabled(False)
        self.pushButtonRemoveProfile.setEnabled(False)
        self.pushButtonStepRemove.setEnabled(False)
        self.pushButtonStepEdit.setEnabled(False)
        self.listWidgetMashProfiles.setCurrentRow(index)
        self.listWidgetSteps.setCurrentRow(i)

    def popMashList(self) :
        self.listWidgetMashProfiles.clear()
        for mash in ImportBase().listeMashes :
           self.listWidgetMashProfiles.addItem(mash.name)

    def mashClicked(self) :
        self.listWidgetSteps.clear()
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            mash = ImportBase().listeMashes[index]
            for step in mash.listeSteps :
                self.listWidgetSteps.addItem(step.name)

            self.labelStepName.setTextFormat(QtCore.Qt.RichText)
            self.labelMashName.setText("<b>" + mash.name + "</b>")
            self.labelMashPh.setText("%.1f" %float(mash.ph))
    #        self.labelMashGrainTemp.setText("%.1f" %float(self.dicMashDetail['grainTemp']))
    #        self.labelMashTunTemp.setText("%.1f" %float(self.dicMashDetail['tunTemp']))
            try :
                self.labelMashSpargeTemp.setText("%.1f" %float(mash.spargeTemp))
            except :
                pass
            try :
                self.listWidgetSteps.setCurrentRow(0)
            except :
                pass
    #        print(self.dicMashDetail)
            self.pushButtonMashEdit.setEnabled(True)
            self.pushButtonRemoveProfile.setEnabled(True)

    def mashDetails(self) :
        self.dlgMashDetail = DialogMashDetail(self)
        self.dlgMashDetail.setModal(True)
        self.dlgMashDetail.show()
        self.dlgMashDetail.setFields(self.currentMash)
        self.dlgMashDetail.setAttribute( QtCore.Qt.WA_DeleteOnClose, True )


    def stepDetails(self) :
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                try:
                    selected_step = selected_mash.listeSteps[i]
                    self.labelStepName.setTextFormat(QtCore.Qt.RichText)
                    self.labelStepName.setText("<b>" + selected_step.name +"</b>")
                    self.labelStepType.setText(selected_step.type)
                    self.labelStepTemp.setText(MashStepView.temp_to_display(selected_step.temp))
                    self.labelStepTime.setText(MashStepView.time_to_display(selected_step.time))
                    self.pushButtonStepRemove.setEnabled(True)
                    self.pushButtonStepEdit.setEnabled(True)
                except:
                    pass


    def stepEdit(self) :
        index = self.listWidgetMashProfiles.currentRow()
        if  index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                selected_step = selected_mash.listeSteps[i]

                self.dlgStep.show()
                self.dlgStep.fields (selected_step)

    def stepReload(self, step) :
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                selected_step = selected_mash.listeSteps[i]

                selected_step.name = step.name
                selected_step.type = step.type
                selected_step.temp = step.temp
                selected_step.time = step.time
                self.seeMash()
                self.stepDetails()
                self.listWidgetMashProfiles.setCurrentRow(index)
                self.listWidgetSteps.setCurrentRow(i)

    def removeStep(self) :
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                item = self.listWidgetSteps.currentItem()
                del selected_mash.listeSteps[i]
                # self.listWidgetSteps.clearSelection()
                #self.listWidgetSteps.takeItem(item)
                #On force la sélection sur la ligne précédente
                self.listWidgetSteps.setCurrentRow(i-1)
                self.seeMash()

    def addStep(self) :
        index = self.listWidgetMashProfiles.currentRow()
        selected_mash = ImportBase().listeMashes[index]
        i = self.listWidgetSteps.currentRow()
        step = MashStep()
        step.name = 'Nouveau palier'
        step.type = 'Infusion'
        step.time = '0'
        step.temp = '0'
        step.vol = '0'
        selected_mash.listeSteps.append(step)

        self.listWidgetMashProfiles.setCurrentRow(index)
        self.seeMash()
        self.stepDetails()
        self.listWidgetMashProfiles.setCurrentRow(index)
        # self.listWidgetSteps.setCurrentRow(i-1)
        # self.stepEdit()

    def mashEdit(self) :
        index = self.listWidgetMashProfiles.currentRow()
        selected_mash = ImportBase().listeMashes[index]
        self.dlgMash.show()
        self.dlgMash.fields(selected_mash)

    def mashReload(self,mash) :
        #on remet le verrou à 0, il va falloir recalculer en repassant en brewday mode
        self.brewdayLock = 0
        f = self.listWidgetMashProfiles.currentRow()
        selected_mash = ImportBase().listeMashes[f]
        selected_mash.name = mash.name
        selected_mash.ph = mash.ph
        selected_mash.grainTemp = 20
        selected_mash.tunTemp = 20
        selected_mash.spargeTemp = mash.spargeTemp
        self.popMashList()
        self.listWidgetMashProfiles.setCurrentRow(f)

    def addMash(self) :
        new_mash = Mash()
        new_mash.name = 'Nouveau profil'
        new_mash.grainTemp = '0'
        new_mash.tunTemp = '0'
        new_mash.spargeTemp = '78'
        new_mash.ph = 5.4
        new_step = MashStep()
        new_step.name = 'Nouveau Palier'
        new_step.type = 'Infusion'
        new_step.time = '0'
        new_step.temp = '0'
        new_mash.listeSteps.append(new_step)
        ImportBase().listeMashes.append(new_mash)
        self.seeMash()
        self.listWidgetMashProfiles.setCurrentRow(len(ImportBase().listeMashes)-1)

    def removeMash(self) :
        i = self.listWidgetMashProfiles.currentRow()
        del ImportBase().listeMashes[i]
        self.seeMash()
        self.listWidgetSteps.clear()

    def mashRejected (self) :
        self.showLib()

    def saveProfile(self) :
        self.mashProfileExport.export(ImportBase().listeMashes)
        self.mashProfileExport.enregistrer(mash_file)

    @QtCore.pyqtSlot()
    def printRecipe (self) :
        printer=QtPrintSupport.QPrinter()
        dialog = QtPrintSupport.QPrintDialog(printer)
        dialog.setModal(True)
        dialog.setWindowTitle("Print Document" )
        if dialog.exec_() == True:
            self.webViewBiblio.print(printer)
            # document=QtGui.QTextDocument()
            # stringHtml=self.recipe.export("print")
            # document.setHtml(stringHtml)
            # document.print(printer)


    @QtCore.pyqtSlot()
    def printBrewday(self) :
        printer=QtPrintSupport.QPrinter()
        dialog = QtPrintSupport.QPrintDialog(printer)
        dialog.setModal(True)
        dialog.setWindowTitle("Print Document" )
        if dialog.exec_() == True:
            self.webViewBrewday.print(printer)
Exemplo n.º 34
0
def backport_records(event, context, **kwargs):
    """Backport records creations, updates and deletions from one collection to another.
    """
    server_url = event["server"]
    source_auth = (
        event.get("backport_records_source_auth")
        or os.environ["BACKPORT_RECORDS_SOURCE_AUTH"]
    )
    source_bucket = (
        event.get("backport_records_source_bucket")
        or os.environ["BACKPORT_RECORDS_SOURCE_BUCKET"]
    )
    source_collection = (
        event.get("backport_records_source_collection")
        or os.environ["BACKPORT_RECORDS_SOURCE_COLLECTION"]
    )

    dest_auth = event.get(
        "backport_records_dest_auth",
        os.getenv("BACKPORT_RECORDS_DEST_AUTH", source_auth),
    )
    dest_bucket = event.get(
        "backport_records_dest_bucket",
        os.getenv("BACKPORT_RECORDS_DEST_BUCKET", source_bucket),
    )
    dest_collection = event.get(
        "backport_records_dest_collection",
        os.getenv("BACKPORT_RECORDS_DEST_COLLECTION", source_collection),
    )

    if source_bucket == dest_bucket and source_collection == dest_collection:
        raise ValueError("Cannot copy records: destination is identical to source")

    source_client = Client(
        server_url=server_url,
        bucket=source_bucket,
        collection=source_collection,
        auth=tuple(source_auth.split(":", 1))
        if ":" in source_auth
        else BearerTokenAuth(source_auth),
    )
    dest_client = Client(
        server_url=server_url,
        bucket=dest_bucket,
        collection=dest_collection,
        auth=tuple(dest_auth.split(":", 1))
        if ":" in dest_auth
        else BearerTokenAuth(dest_auth),
    )

    source_timestamp = source_client.get_records_timestamp()
    dest_timestamp = dest_client.get_records_timestamp()
    if source_timestamp <= dest_timestamp:
        print("Records are in sync. Nothing to do.")
        return

    source_records = source_client.get_records()
    dest_records_by_id = {r["id"]: r for r in dest_client.get_records()}

    with dest_client.batch() as dest_batch:
        # Create or update the destination records.
        for r in source_records:
            dest_record = dest_records_by_id.pop(r["id"], None)
            if dest_record is None:
                dest_batch.create_record(data=r)
            elif r["last_modified"] > dest_record["last_modified"]:
                dest_batch.update_record(data=r)
        # Delete the records missing from source.
        for r in dest_records_by_id.values():
            dest_batch.delete_record(id=r["id"])

    ops_count = len(dest_batch.results())

    # If destination has signing, request review or auto-approve changes.
    server_info = dest_client.server_info()
    signer_config = server_info["capabilities"].get("signer", {})
    signer_resources = signer_config.get("resources", [])
    # Check destination collection config (sign-off required etc.)
    signed_dest = [
        r
        for r in signer_resources
        if r["source"]["bucket"] == dest_bucket
        and r["source"]["collection"] == dest_collection
    ]
    if len(signed_dest) == 0:
        # Not explicitly configured. Check if configured at bucket level?
        signed_dest = [
            r
            for r in signer_resources
            if r["source"]["bucket"] == dest_bucket
            and r["source"]["collection"] is None
        ]
    # Destination has no signature enabled. Nothing to do.
    if len(signed_dest) == 0:
        print(f"Done. {ops_count} changes applied.")
        return

    has_autoapproval = not signed_dest[0].get(
        "to_review_enabled", signer_config["to_review_enabled"]
    ) and not signed_dest[0].get(
        "group_check_enabled", signer_config["group_check_enabled"]
    )
    if has_autoapproval:
        # Approve the changes.
        dest_client.patch_collection(data={"status": "to-sign"})
        print(f"Done. {ops_count} changes applied and signed.")
    else:
        # Request review.
        dest_client.patch_collection(data={"status": "to-review"})
        print(f"Done. Requested review for {ops_count} changes.")
Exemplo n.º 35
0
class RecordTest(unittest.TestCase):
    def setUp(self):
        self.session = mock.MagicMock()
        self.session.request.return_value = (mock.sentinel.response, mock.sentinel.count)
        self.client = Client(
            session=self.session, bucket='mybucket',
            collection='mycollection')

    def test_record_id_is_given_after_creation(self):
        mock_response(self.session, data={'id': 5678})
        record = self.client.create_record(data={'foo': 'bar'})
        assert 'id' in record['data'].keys()

    def test_generated_record_id_is_an_uuid(self):
        mock_response(self.session)
        self.client.create_record(data={'foo': 'bar'})
        id = self.session.request.mock_calls[0][1][1].split('/')[-1]

        uuid_regexp = r'[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}'
        self.assertRegex(id, uuid_regexp)

    def test_records_handles_permissions(self):
        mock_response(self.session)
        self.client.create_record(data={'id': '1234', 'foo': 'bar'},
                                  permissions=mock.sentinel.permissions)
        self.session.request.assert_called_with(
            'put',
            '/buckets/mybucket/collections/mycollection/records/1234',
            data={'foo': 'bar', 'id': '1234'},
            permissions=mock.sentinel.permissions,
            headers=DO_NOT_OVERWRITE)

    def test_collection_argument_takes_precedence(self):
        mock_response(self.session)
        # Specify a different collection name for the client and the operation.
        client = Client(session=self.session, bucket='mybucket',
                        collection='wrong_collection')
        client.update_record(data={'id': '1234'}, collection='good_collection',
                             permissions=mock.sentinel.permissions)

        self.session.request.assert_called_with(
            'put',
            '/buckets/mybucket/collections/good_collection/records/1234',
            data={'id': '1234'},
            headers=None,
            permissions=mock.sentinel.permissions)

    def test_record_id_is_derived_from_data_if_present(self):
        mock_response(self.session)
        self.client.create_record(data={'id': '1234', 'foo': 'bar'},
                                  permissions=mock.sentinel.permissions)

        self.session.request.assert_called_with(
            'put',
            '/buckets/mybucket/collections/mycollection/records/1234',
            data={'id': '1234', 'foo': 'bar'},
            permissions=mock.sentinel.permissions,
            headers=DO_NOT_OVERWRITE)

    def test_data_and_permissions_are_added_on_create(self):
        mock_response(self.session)
        data = {'foo': 'bar'}
        permissions = {'read': ['mle']}

        self.client.create_record(id='1234',
                                  data=data,
                                  permissions=permissions)

        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with(
            'put', url, data=data, permissions=permissions,
            headers=DO_NOT_OVERWRITE)

    def test_creation_sends_if_none_match_by_default(self):
        mock_response(self.session)
        data = {'foo': 'bar'}

        self.client.create_record(id='1234', data=data)

        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with(
            'put', url, data=data, permissions=None, headers=DO_NOT_OVERWRITE)

    def test_creation_doesnt_add_if_none_match_when_overwrite(self):
        mock_response(self.session)
        data = {'foo': 'bar'}

        self.client.create_record(id='1234', data=data, safe=False)

        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with(
            'put', url, data=data, permissions=None, headers=None)

    def test_records_issues_a_request_on_delete(self):
        mock_response(self.session)
        self.client.delete_record(id='1234')
        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with('delete', url, headers=None)

    def test_record_issues_a_request_on_retrieval(self):
        mock_response(self.session, data={'foo': 'bar'})
        record = self.client.get_record(id='1234')

        self.assertEqual(record['data'], {'foo': 'bar'})
        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with('get', url)

    def test_collection_can_retrieve_all_records(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}])
        records = self.client.get_records()
        assert list(records) == [{'id': 'foo'}, {'id': 'bar'}]

    def test_collection_can_retrieve_records_timestamp(self):
        mock_response(self.session, headers={"ETag": '"12345"'})
        timestamp = self.client.get_records_timestamp()
        assert timestamp == '12345'

    def test_records_timestamp_is_cached(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"12345"'})
        self.client.get_records()
        timestamp = self.client.get_records_timestamp()
        assert timestamp == '12345'
        assert self.session.request.call_count == 1

    def test_records_timestamp_is_cached_per_collection(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"12345"'})
        self.client.get_records(collection="foo")
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"67890"'})
        self.client.get_records(collection="bar")

        timestamp = self.client.get_records_timestamp(collection="foo")
        assert timestamp == '12345'

        timestamp = self.client.get_records_timestamp(collection="bar")
        assert timestamp == '67890'

    def test_pagination_is_followed(self):
        # Mock the calls to request.
        link = ('http://example.org/buckets/buck/collections/coll/records/'
                '?token=1234')

        self.session.request.side_effect = [
            # First one returns a list of items with a pagination token.
            build_response(
                [{'id': '1', 'value': 'item1'},
                 {'id': '2', 'value': 'item2'}, ],
                {'Next-Page': link}),
            build_response(
                [{'id': '3', 'value': 'item3'},
                 {'id': '4', 'value': 'item4'}, ],
                {'Next-Page': link}),
            # Second one returns a list of items without a pagination token.
            build_response(
                [{'id': '5', 'value': 'item5'},
                 {'id': '6', 'value': 'item6'}, ],
            ),
        ]
        records = self.client.get_records(bucket='bucket', collection='collection')

        assert list(records) == [
            {'id': '1', 'value': 'item1'},
            {'id': '2', 'value': 'item2'},
            {'id': '3', 'value': 'item3'},
            {'id': '4', 'value': 'item4'},
            {'id': '5', 'value': 'item5'},
            {'id': '6', 'value': 'item6'},
        ]

    def test_pagination_is_followed_for_number_of_pages(self):
        # Mock the calls to request.
        link = ('http://example.org/buckets/buck/collections/coll/records/'
                '?token=1234')

        self.session.request.side_effect = [
            # First one returns a list of items with a pagination token.
            build_response(
                [{'id': '1', 'value': 'item1'},
                 {'id': '2', 'value': 'item2'}, ],
                {'Next-Page': link}),
            build_response(
                [{'id': '3', 'value': 'item3'},
                 {'id': '4', 'value': 'item4'}, ],
                {'Next-Page': link}),
            # Second one returns a list of items without a pagination token.
            build_response(
                [{'id': '5', 'value': 'item5'},
                 {'id': '6', 'value': 'item6'}, ],
            ),
        ]
        records = self.client.get_records(bucket='bucket', collection='collection', pages=2)

        assert list(records) == [
            {'id': '1', 'value': 'item1'},
            {'id': '2', 'value': 'item2'},
            {'id': '3', 'value': 'item3'},
            {'id': '4', 'value': 'item4'},
        ]

    def test_pagination_is_not_followed_if_limit_is_specified(self):
        # Mock the calls to request.
        link = ('http://example.org/buckets/buck/collections/coll/records/'
                '?token=1234')

        self.session.request.side_effect = [
            build_response(
                [{'id': '1', 'value': 'item1'},
                 {'id': '2', 'value': 'item2'}, ],
                {'Next-Page': link}),
            build_response(
                [{'id': '3', 'value': 'item3'},
                 {'id': '4', 'value': 'item4'}, ],
            ),
        ]
        records = self.client.get_records(bucket='bucket', collection='collection', _limit=2)

        assert list(records) == [
            {'id': '1', 'value': 'item1'},
            {'id': '2', 'value': 'item2'}
        ]

    def test_pagination_supports_if_none_match(self):
        link = ('http://example.org/buckets/buck/collections/coll/records/'
                '?token=1234')

        self.session.request.side_effect = [
            # First one returns a list of items with a pagination token.
            build_response(
                [{'id': '1', 'value': 'item1'},
                 {'id': '2', 'value': 'item2'}, ],
                {'Next-Page': link}),
            # Second one returns a list of items without a pagination token.
            build_response(
                [{'id': '3', 'value': 'item3'},
                 {'id': '4', 'value': 'item4'}, ],
            ),
        ]
        self.client.get_records(bucket='bucket', collection='collection',
                                if_none_match="1234")

        # Check that the If-None-Match header is present in the requests.
        self.session.request.assert_any_call(
            'get', '/buckets/bucket/collections/collection/records',
            headers={'If-None-Match': '"1234"'}, params={})
        self.session.request.assert_any_call(
            'get', link, headers={'If-None-Match': '"1234"'}, params={})

    def test_collection_can_delete_a_record(self):
        mock_response(self.session, data={'id': 1234})
        resp = self.client.delete_record(id=1234)
        assert resp == {'id': 1234}
        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with('delete', url, headers=None)

    def test_record_delete_if_match(self):
        data = {}
        mock_response(self.session, data=data)
        deleted = self.client.delete_record(collection='mycollection',
                                            bucket='mybucket',
                                            id='1',
                                            if_match=1234)
        assert deleted == data
        url = '/buckets/mybucket/collections/mycollection/records/1'
        self.session.request.assert_called_with(
            'delete', url, headers={'If-Match': '"1234"'})

    def test_record_delete_if_match_not_included_if_not_safe(self):
        data = {}
        mock_response(self.session, data=data)
        deleted = self.client.delete_record(collection='mycollection',
                                            bucket='mybucket',
                                            id='1',
                                            if_match=1234,
                                            safe=False)
        assert deleted == data
        url = '/buckets/mybucket/collections/mycollection/records/1'
        self.session.request.assert_called_with(
            'delete', url, headers=None)

    def test_update_record_gets_the_id_from_data_if_exists(self):
        mock_response(self.session)
        self.client.update_record(bucket='mybucket', collection='mycollection',
                                  data={'id': 1, 'foo': 'bar'})

        self.session.request.assert_called_with(
            'put', '/buckets/mybucket/collections/mycollection/records/1',
            data={'id': 1, 'foo': 'bar'}, headers=None, permissions=None)

    def test_update_record_handles_if_match(self):
        mock_response(self.session)
        self.client.update_record(bucket='mybucket', collection='mycollection',
                                  data={'id': 1, 'foo': 'bar'}, if_match=1234)

        headers = {'If-Match': '"1234"'}
        self.session.request.assert_called_with(
            'put', '/buckets/mybucket/collections/mycollection/records/1',
            data={'id': 1, 'foo': 'bar'}, headers=headers, permissions=None)

    def test_patch_record_uses_the_patch_method(self):
        mock_response(self.session)
        self.client.patch_record(bucket='mybucket', collection='mycollection',
                                 data={'id': 1, 'foo': 'bar'})

        self.session.request.assert_called_with(
            'patch', '/buckets/mybucket/collections/mycollection/records/1',
            payload={'data': {'id': 1, 'foo': 'bar'}},
            headers={"Content-Type": "application/json"})

    def test_patch_record_recognizes_patchtype(self):
        mock_response(self.session)
        self.client.patch_record(bucket='mybucket', collection='mycollection',
                                 changes=MergePatch({'foo': 'bar'}, {'read': ['alice']}), id=1)

        self.session.request.assert_called_with(
            'patch', '/buckets/mybucket/collections/mycollection/records/1',
            payload={'data': {'foo': 'bar'}, 'permissions': {'read': ['alice']}},
            headers={"Content-Type": "application/merge-patch+json"},
        )

    def test_patch_record_understands_jsonpatch(self):
        mock_response(self.session)
        self.client.patch_record(
            bucket='mybucket', collection='mycollection',
            changes=JSONPatch([{'op': 'add', 'patch': '/baz', 'value': 'qux'}]), id=1)

        self.session.request.assert_called_with(
            'patch', '/buckets/mybucket/collections/mycollection/records/1',
            payload=[{'op': 'add', 'patch': '/baz', 'value': 'qux'}],
            headers={"Content-Type": "application/json-patch+json"},
        )

    def test_patch_record_requires_data_to_be_patch_type(self):
        with pytest.raises(TypeError, match="couldn't understand patch body 5"):
            self.client.patch_record(id=1, collection='testcoll', bucket='testbucket', changes=5)

    def test_patch_record_requires_id(self):
        with pytest.raises(KeyError, match="Unable to patch record, need an id."):
            self.client.patch_record(collection='testcoll', bucket='testbucket', data={})

    def test_update_record_raises_if_no_id_is_given(self):
        with self.assertRaises(KeyError) as cm:
            self.client.update_record(data={'foo': 'bar'},  # Omit the id on purpose here.
                                      bucket='mybucket',
                                      collection='mycollection')
        assert str(cm.exception) == "'Unable to update a record, need an id.'"

    def test_get_or_create_doesnt_raise_in_case_of_conflict(self):
        data = {
            'permissions': mock.sentinel.permissions,
            'data': {'foo': 'bar'}
        }
        self.session.request.side_effect = [
            get_http_error(status=412),
            (data, None)
        ]
        returned_data = self.client.create_record(bucket="buck",
                                                  collection="coll",
                                                  data={'id': 1234,
                                                        'foo': 'bar'},
                                                  if_not_exists=True)  # Should not raise.
        assert returned_data == data

    def test_get_or_create_raise_in_other_cases(self):
        self.session.request.side_effect = get_http_error(status=500)
        with self.assertRaises(KintoException):
            self.client.create_record(bucket="buck",
                                      collection="coll",
                                      data={'foo': 'bar'},
                                      id='record',
                                      if_not_exists=True)

    def test_create_record_raises_a_special_error_on_403(self):
        self.session.request.side_effect = get_http_error(status=403)
        with self.assertRaises(KintoException) as e:
            self.client.create_record(bucket="buck",
                                      collection="coll",
                                      data={'foo': 'bar'})
        expected_msg = ("Unauthorized. Please check that the collection exists"
                        " and that you have the permission to create or write "
                        "on this collection record.")
        assert e.exception.message == expected_msg

    def test_create_record_can_deduce_id_from_data(self):
        self.client.create_record(data={'id': 'record'}, bucket='buck', collection='coll')
        self.session.request.assert_called_with(
            'put', '/buckets/buck/collections/coll/records/record', data={'id': 'record'},
            permissions=None, headers=DO_NOT_OVERWRITE)

    def test_update_record_can_deduce_id_from_data(self):
        self.client.update_record(data={'id': 'record'}, bucket='buck', collection='coll')
        self.session.request.assert_called_with(
            'put', '/buckets/buck/collections/coll/records/record', data={'id': 'record'},
            permissions=None, headers=None)
def validate_signature(event, context, **kwargs):
    """Validate the signature of each collection.
    """
    server_url = event["server"]
    bucket = event.get("bucket", "monitor")
    collection = event.get("collection", "changes")
    client = Client(server_url=server_url,
                    bucket=bucket,
                    collection=collection)
    print("Read collection list from {}".format(
        client.get_endpoint("collection")))

    error_messages = []

    checked_certificates = {}

    collections = client.get_records()

    # Grab server data in parallel.
    start_time = time.time()
    collections_data = []
    with concurrent.futures.ThreadPoolExecutor(
            max_workers=PARALLEL_REQUESTS) as executor:
        futures = [
            executor.submit(download_collection_data, server_url, c)
            for c in collections
        ]
        for future in concurrent.futures.as_completed(futures):
            collections_data.append(future.result())
    elapsed_time = time.time() - start_time
    print(f"Downloaded all data in {elapsed_time:.2f}s")

    for i, (collection, endpoint, metadata, records,
            timestamp) in enumerate(collections_data):
        start_time = time.time()

        message = "{:02d}/{:02d} {}:  ".format(i + 1, len(collections),
                                               endpoint)

        # 1. Serialize
        serialized = canonical_json(records, timestamp)
        data = b"Content-Signature:\x00" + serialized.encode("utf-8")

        # 2. Grab the signature
        try:
            signature = metadata["signature"]
        except KeyError:
            # Destination has no signature attribute.
            # Be smart and check if it was just configured.
            # See https://github.com/mozilla-services/remote-settings-lambdas/issues/31
            client = Client(
                server_url=server_url,
                bucket=collection["bucket"],
                collection=collection["collection"],
            )
            with_tombstones = client.get_records(_since=1)
            if len(with_tombstones) == 0:
                # It never contained records. Let's assume it is newly configured.
                message += "SKIP"
                print(message)
                continue
            # Some records and empty signature? It will fail below.
            signature = {}

        try:
            # 3. Verify the signature with the public key
            pubkey = signature["public_key"].encode("utf-8")
            verifier = ecdsa.VerifyingKey.from_pem(pubkey)
            signature_bytes = base64.urlsafe_b64decode(signature["signature"])
            verified = verifier.verify(signature_bytes,
                                       data,
                                       hashfunc=hashlib.sha384)
            assert verified, "Signature verification failed"

            # 4. Verify that the x5u certificate is valid (ie. that signature was well refreshed)
            x5u = signature["x5u"]
            if x5u not in checked_certificates:
                resp = requests.get(signature["x5u"])
                cert_pem = resp.text.encode("utf-8")
                cert = cryptography.x509.load_pem_x509_certificate(
                    cert_pem, crypto_default_backend())
                assert (cert.not_valid_before <
                        datetime.now()), "certificate not yet valid"
                assert cert.not_valid_after > datetime.now(
                ), "certificate expired"
                subject = cert.subject.get_attributes_for_oid(
                    NameOID.COMMON_NAME)[0].value
                # eg. ``onecrl.content-signature.mozilla.org``, or
                # ``pinning-preload.content-signature.mozilla.org``
                assert subject.endswith(
                    ".content-signature.mozilla.org"), "invalid subject name"
                checked_certificates[x5u] = cert

            # 5. Check that public key matches the certificate one.
            cert = checked_certificates[x5u]
            cert_pubkey_pem = cert.public_key().public_bytes(
                crypto_serialization.Encoding.PEM,
                crypto_serialization.PublicFormat.SubjectPublicKeyInfo,
            )
            assert (unpem(cert_pubkey_pem) == pubkey
                    ), "signature public key does not match certificate"

            elapsed_time = time.time() - start_time
            message += f"OK ({elapsed_time:.2f}s)"
            print(message)
        except Exception:
            message += "⚠ BAD Signature ⚠"
            print(message)

            # Gather details for the global exception that will be raised.
            signed_on = metadata["last_modified"]
            signed_on_date = timestamp_to_date(signed_on)
            timestamp_date = timestamp_to_date(timestamp)
            error_message = (
                "Signature verification failed on {endpoint}\n"
                " - Signed on: {signed_on} ({signed_on_date})\n"
                " - Records timestamp: {timestamp} ({timestamp_date})").format(
                    **locals())
            error_messages.append(error_message)

    # Make the lambda to fail in case an exception occured
    if len(error_messages) > 0:
        raise ValidationError("\n" + "\n\n".join(error_messages))
            result += ','
        result += '\n'

        i += 1

    result += ']'

    return result


for collection in collections:
    name = collection['id']

    print(name + '...')

    record_list = client.get_records(collection=name, bucket='pesquisa')
    records = record_list[0]['info']

    if len(record_list) > 1:
        print('found ' + str(len(record_list)) + ' entries.')

    for record in records:
        voto = Voto()

        voto.pesquisador = name

        voto.genero = record['genero']
        voto.classe = record['classe_social']

        voto.candidato = record['candidato']
        voto.rejeita = record['rejeita']