예제 #1
0
    def testEmpty(self):
        empty_hash = CaseStateHash(EMPTY_HASH)
        wrong_hash = CaseStateHash("thisisntright")
        self.assertEqual(empty_hash, self.sync_log.get_state_hash())
        response = generate_restore_response(self.project,
                                             self.user,
                                             self.sync_log.get_id,
                                             version=V2)
        self.assertEqual(200, response.status_code)

        try:
            generate_restore_payload(self.project,
                                     self.user,
                                     self.sync_log.get_id,
                                     version=V2,
                                     state_hash=str(wrong_hash))
            self.fail(
                "Call to generate a payload with a bad hash should fail!")
        except BadStateException as e:
            self.assertEqual(empty_hash, e.server_hash)
            self.assertEqual(wrong_hash, e.phone_hash)
            self.assertEqual(0, len(e.case_ids))

        response = generate_restore_response(self.project,
                                             self.user,
                                             self.sync_log.get_id,
                                             version=V2,
                                             state_hash=str(wrong_hash))
        self.assertEqual(412, response.status_code)
    def testMismatch(self):
        sync = self.device.last_sync
        self.assertEqual(CaseStateHash(EMPTY_HASH), sync.log.get_state_hash())

        c1 = CaseBlock(case_id="abc123",
                       create=True,
                       owner_id=self.user.user_id)
        c2 = CaseBlock(case_id="123abc",
                       create=True,
                       owner_id=self.user.user_id)
        self.device.post_changes([c1, c2])

        real_hash = CaseStateHash("409c5c597fa2c2a693b769f0d2ad432b")
        bad_hash = CaseStateHash("thisisntright")
        self.assertEqual(real_hash, sync.get_log().get_state_hash())
        self.device.sync(state_hash=str(real_hash))

        self.device.last_sync = sync
        try:
            self.device.sync(state_hash=str(bad_hash))
        except BadStateException as e:
            self.assertEqual(real_hash, e.server_hash)
            self.assertEqual(bad_hash, e.phone_hash)
            self.assertEqual(set(e.case_ids), {"abc123", "123abc"})
        else:
            self.fail(
                "Call to generate a payload with a bad hash should fail!")
예제 #3
0
 def testMismatch(self):
     self.assertEqual(CaseStateHash(EMPTY_HASH), self.sync_log.get_state_hash())
     
     c1 = CaseBlock(case_id="abc123", create=True, 
                    owner_id=self.user.user_id).as_xml()
     c2 = CaseBlock(case_id="123abc", create=True, 
                    owner_id=self.user.user_id).as_xml()
     post_case_blocks([c1, c2], 
                      form_extras={"last_sync_token": self.sync_log.get_id})
     
     self.sync_log = get_properly_wrapped_sync_log(self.sync_log.get_id)
     real_hash = CaseStateHash("409c5c597fa2c2a693b769f0d2ad432b")
     bad_hash = CaseStateHash("thisisntright")
     self.assertEqual(real_hash, self.sync_log.get_state_hash())
     generate_restore_payload(
         self.project, self.user, self.sync_log.get_id,
         version=V2, state_hash=str(real_hash)
     )
     
     try:
         generate_restore_payload(self.project, self.user, self.sync_log.get_id,
                                  version=V2, state_hash=str(bad_hash))
         self.fail("Call to generate a payload with a bad hash should fail!")
     except BadStateException, e:
         self.assertEqual(real_hash, e.server_hash)
         self.assertEqual(bad_hash, e.phone_hash)
         self.assertEqual(2, len(e.case_ids))
         self.assertTrue("abc123" in e.case_ids)
         self.assertTrue("123abc" in e.case_ids)
예제 #4
0
    def validate_state(self):
        check_version(self.params.version)
        if self.last_sync_log:
            if self.params.state_hash:
                parsed_hash = CaseStateHash.parse(self.params.state_hash)
                computed_hash = self.last_sync_log.get_state_hash()
                if computed_hash != parsed_hash:
                    # log state error on the sync log
                    self.last_sync_log.had_state_error = True
                    self.last_sync_log.error_date = datetime.utcnow()
                    self.last_sync_log.error_hash = str(parsed_hash)
                    self.last_sync_log.save()

                    exception = BadStateException(
                        server_hash=computed_hash,
                        phone_hash=parsed_hash,
                        case_ids=self.last_sync_log.
                        get_footprint_of_cases_on_phone())
                    if self.last_sync_log.log_format == LOG_FORMAT_SIMPLIFIED:
                        from corehq.apps.reports.standard.deployments import SyncHistoryReport
                        _assert = soft_assert(to=['czue' + '@' + 'dimagi.com'])
                        sync_history_url = '{}?individual={}'.format(
                            SyncHistoryReport.get_url(self.domain),
                            self.user.user_id)
                        _assert(
                            False, '{}, sync history report: {}'.format(
                                exception, sync_history_url))
                    raise exception
예제 #5
0
    def validate_state(self):
        check_version(self.params.version)
        if self.last_sync_log:
            if self.params.state_hash:
                parsed_hash = CaseStateHash.parse(self.params.state_hash)
                computed_hash = self.last_sync_log.get_state_hash()
                if computed_hash != parsed_hash:
                    # log state error on the sync log
                    self.last_sync_log.had_state_error = True
                    self.last_sync_log.error_date = datetime.utcnow()
                    self.last_sync_log.error_hash = str(parsed_hash)
                    self.last_sync_log.save()

                    exception = BadStateException(
                        server_hash=computed_hash,
                        phone_hash=parsed_hash,
                        case_ids=self.last_sync_log.get_footprint_of_cases_on_phone()
                    )
                    if self.last_sync_log.log_format == LOG_FORMAT_SIMPLIFIED:
                        from corehq.apps.reports.standard.deployments import SyncHistoryReport
                        _assert = soft_assert(to=['czue' + '@' + 'dimagi.com'])
                        sync_history_url = '{}?individual={}'.format(
                            SyncHistoryReport.get_url(self.domain),
                            self.user.user_id
                        )
                        _assert(False, '{}, sync history report: {}'.format(exception, sync_history_url))
                    raise exception
예제 #6
0
 def validate(self):
     # runs validation checks, raises exceptions if anything is amiss
     check_version(self.version)
     if self.sync_log and self.state_hash:
         parsed_hash = CaseStateHash.parse(self.state_hash)
         if self.sync_log.get_state_hash() != parsed_hash:
             raise BadStateException(expected=self.sync_log.get_state_hash(),
                                     actual=parsed_hash,
                                     case_ids=self.sync_log.get_footprint_of_cases_on_phone())
예제 #7
0
    def testEmpty(self):
        empty_hash = CaseStateHash(EMPTY_HASH)
        wrong_hash = CaseStateHash("thisisntright")
        self.assertEqual(empty_hash, self.device.last_sync.log.get_state_hash())
        response = self.device.get_restore_config().get_response()
        self.assertEqual(200, response.status_code)

        config = self.device.get_restore_config(state_hash=str(wrong_hash))
        try:
            config.get_payload()
        except BadStateException as e:
            self.assertEqual(empty_hash, e.server_hash)
            self.assertEqual(wrong_hash, e.phone_hash)
            self.assertEqual(0, len(e.case_ids))
        else:
            self.fail("Call to generate a payload with a bad hash should fail!")

        self.assertEqual(412, config.get_response().status_code)
예제 #8
0
 def validate(self):
     # runs validation checks, raises exceptions if anything is amiss
     check_version(self.version)
     if self.sync_log and self.state_hash:
         parsed_hash = CaseStateHash.parse(self.state_hash)
         if self.sync_log.get_state_hash() != parsed_hash:
             raise BadStateException(expected=self.sync_log.get_state_hash(),
                                     actual=parsed_hash,
                                     case_ids=self.sync_log.get_footprint_of_cases_on_phone())
예제 #9
0
 def validate_state(self):
     check_version(self.params.version)
     if self.last_sync_log:
         if not isinstance(self.last_sync_log, self.sync_log_class):
             raise IncompatibleSyncLogType('Unable to convert from {} to {}'.format(
                 type(self.last_sync_log), self.sync_log_class,
             ))
         if self.params.state_hash:
             parsed_hash = CaseStateHash.parse(self.params.state_hash)
             computed_hash = self.last_sync_log.get_state_hash()
             if computed_hash != parsed_hash:
                 raise BadStateException(expected=computed_hash,
                                         actual=parsed_hash,
                                         case_ids=self.last_sync_log.get_footprint_of_cases_on_phone())
예제 #10
0
    def validate_state(self):
        check_version(self.params.version)
        if self.last_sync_log:
            if self.params.state_hash:
                parsed_hash = CaseStateHash.parse(self.params.state_hash)
                computed_hash = self.last_sync_log.get_state_hash()
                if computed_hash != parsed_hash:
                    # log state error on the sync log
                    self.last_sync_log.had_state_error = True
                    self.last_sync_log.error_date = datetime.utcnow()
                    self.last_sync_log.error_hash = str(parsed_hash)
                    self.last_sync_log.save()

                    raise BadStateException(server_hash=computed_hash,
                                            phone_hash=parsed_hash,
                                            case_ids=self.last_sync_log.
                                            get_footprint_of_cases_on_phone())
예제 #11
0
    def validate_state(self):
        check_version(self.params.version)
        if self.last_sync_log:
            if self.params.state_hash:
                parsed_hash = CaseStateHash.parse(self.params.state_hash)
                computed_hash = self.last_sync_log.get_state_hash()
                if computed_hash != parsed_hash:
                    # log state error on the sync log
                    self.last_sync_log.had_state_error = True
                    self.last_sync_log.error_date = datetime.utcnow()
                    self.last_sync_log.error_hash = str(parsed_hash)
                    self.last_sync_log.save()

                    raise BadStateException(
                        server_hash=computed_hash,
                        phone_hash=parsed_hash,
                        case_ids=self.last_sync_log.get_footprint_of_cases_on_phone()
                    )
예제 #12
0
    def validate_state(self):
        check_version(self.params.version)
        if self.last_sync_log:
            if (self._case_sync == CLEAN_OWNERS and
                    self.last_sync_log.log_format == LOG_FORMAT_LIVEQUERY):
                raise RestoreException("clean_owners sync after livequery sync")
            if self.params.state_hash:
                parsed_hash = CaseStateHash.parse(self.params.state_hash)
                computed_hash = self.last_sync_log.get_state_hash()
                if computed_hash != parsed_hash:
                    # log state error on the sync log
                    self.last_sync_log.had_state_error = True
                    self.last_sync_log.error_date = datetime.utcnow()
                    self.last_sync_log.error_hash = str(parsed_hash)
                    self.last_sync_log.save()

                    raise BadStateException(
                        server_hash=computed_hash,
                        phone_hash=parsed_hash,
                        case_ids=self.last_sync_log.get_footprint_of_cases_on_phone()
                    )
예제 #13
0
    def validate_state(self):
        check_version(self.params.version)
        if self.last_sync_log:
            if (self._case_sync == CLEAN_OWNERS and
                    self.last_sync_log.log_format == LOG_FORMAT_LIVEQUERY):
                raise RestoreException("clean_owners sync after livequery sync")
            if self.params.state_hash:
                parsed_hash = CaseStateHash.parse(self.params.state_hash)
                computed_hash = self.last_sync_log.get_state_hash()
                if computed_hash != parsed_hash:
                    # log state error on the sync log
                    self.last_sync_log.had_state_error = True
                    self.last_sync_log.error_date = datetime.utcnow()
                    self.last_sync_log.error_hash = str(parsed_hash)
                    self.last_sync_log.save()

                    raise BadStateException(
                        server_hash=computed_hash,
                        phone_hash=parsed_hash,
                        case_ids=self.last_sync_log.get_footprint_of_cases_on_phone()
                    )
예제 #14
0
    def validate_state(self):
        check_version(self.params.version)
        if self.last_sync_log:
            if self.params.state_hash:
                parsed_hash = CaseStateHash.parse(self.params.state_hash)
                computed_hash = self.last_sync_log.get_state_hash()
                if computed_hash != parsed_hash:
                    # log state error on the sync log
                    self.last_sync_log.had_state_error = True
                    self.last_sync_log.error_date = datetime.utcnow()
                    self.last_sync_log.error_hash = str(parsed_hash)
                    self.last_sync_log.save()

                    exception = BadStateException(
                        expected=computed_hash,
                        actual=parsed_hash,
                        case_ids=self.last_sync_log.get_footprint_of_cases_on_phone()
                    )
                    if self.last_sync_log.log_format == LOG_FORMAT_SIMPLIFIED:
                        _assert = soft_assert(to=['czue' + '@' + 'dimagi.com'])
                        _assert(False, str(exception))
                    raise exception
예제 #15
0
 def get_state_hash(self):
     return CaseStateHash(
         Checksum(self.get_footprint_of_cases_on_phone()).hexdigest())
예제 #16
0
def generate_restore_payload(user, restore_id="", version="1.0", state_hash=""):
    """
    Gets an XML payload suitable for OTA restore. If you need to do something
    other than find all cases matching user_id = user.user_id then you have
    to pass in a user object that overrides the get_case_updates() method.
    
    It should match the same signature as models.user.get_case_updates():
    
        user:          who the payload is for. must implement get_case_updates
        restore_id:    sync token
        version:       the CommCare version 
        
        returns: the xml payload of the sync operation
    """
    check_version(version)
    
    last_sync = None
    if restore_id:
        try:
            last_sync = SyncLog.get(restore_id)
        except Exception:
            logging.error("Request for bad sync log %s by %s, ignoring..." % (restore_id, user))
    
    if last_sync and state_hash:
        parsed_hash = CaseStateHash.parse(state_hash)
        if last_sync.get_state_hash() != parsed_hash:
            raise BadStateException(expected=last_sync.get_state_hash(), 
                                    actual=parsed_hash,
                                    case_ids=last_sync.get_footprint_of_cases_on_phone())
        
    sync_operation = user.get_case_updates(last_sync)
    case_xml_elements = [xml.get_case_element(op.case, op.required_updates, version) \
                         for op in sync_operation.actual_cases_to_sync]
    
    
    last_seq = get_db().info()["update_seq"]
    
    # create a sync log for this
    previous_log_id = last_sync.get_id if last_sync else None
    
    synclog = SyncLog(user_id=user.user_id, last_seq=last_seq,
                      owner_ids_on_phone=user.get_owner_ids(),
                      date=datetime.utcnow(), previous_log_id=previous_log_id,
                      cases_on_phone=[CaseState.from_case(c) for c in \
                                      sync_operation.actual_owned_cases],
                      dependent_cases_on_phone=[CaseState.from_case(c) for c in \
                                                sync_operation.actual_extended_cases])
    synclog.save()
    
    # start with standard response
    response = get_response_element(
        "Successfully restored account %s!" % user.username, 
        ResponseNature.OTA_RESTORE_SUCCESS)
    
    # add sync token info
    response.append(xml.get_sync_element(synclog.get_id))
    # registration block
    response.append(xml.get_registration_element(user))
    # fixture block
    for fixture in generator.get_fixtures(user, version, last_sync):
        response.append(fixture)
    # case blocks
    for case_elem in case_xml_elements:
        response.append(case_elem)
    
    return xml.tostring(response)