def __init__(self, plan_id=None, project_id=None, uri=None, suds_object=None): """Plan Constructor Args: plan_id: ID of the plan project_id: ID of the project that contains the specific plan required when plan_id is given uri: the SubterraURI of the plan to load suds_object: the Polarion Plan object Returns: None References: Planning.getPlanById Planning.getPlanByUri """ super(self.__class__, self).__init__(obj_id=plan_id, suds_object=suds_object) if plan_id: if not project_id: raise PyleroLibException("When plan_id is passed in, " "project_id is required") self._suds_object = self.session.planning_client.service. \ getPlanById(project_id, plan_id) elif uri: self._suds_object = self.session.planning_client.service. \ getPlanByUri(uri) if plan_id or uri: if getattr(self._suds_object, "_unresolvable", True): raise PyleroLibException( "The Plan {0} was not found.".format(plan_id))
def check_valid_field_values(self, val, enum_id, additional_parms, control=None): """verifies id the value passed in is valid for the enum or object passed in. for example, if we want to see if a valid user is given, this will try to instantiate the User class with the given parameter and additional parms. If it fails, it is not a valid value. Args: val: the value you want to set it to. enum_id: the enumeration or object to validate against additional_parms (dict): parms needed to instantiate class passed in as enum_id control: the control key for the enumeration. default:None """ if isinstance(enum_id, type): try: # try to instantiate the object with the value and additional # parms. If that works, it is a valid value enum_id(val, **additional_parms) except Exception: raise PyleroLibException( "{0} is not a valid value for {1}" .format(val, enum_id.__name__)) else: valid_values = self.get_valid_field_values(enum_id, control) if val not in valid_values: raise PyleroLibException("Acceptable values for {0} are:" "{1}".format(enum_id, valid_values))
def __init__(self): defaults = {"cachingpolicy": "0", "timeout": "120"} config = SafeConfigParser(defaults) # Check for existence of config file and config_section if not config.read([self.GLOBAL_CONFIG, self.LOCAL_CONFIG, self.CURDIR_CONFIG]) or \ not config.has_section(self.CONFIG_SECTION): # Check for mandatory environ variables if config file is not found if not all(os.environ.get(item) for item in ('POLARION_URL', 'POLARION_REPO', 'POLARION_USERNAME', 'POLARION_PASSWORD', 'POLARION_PROJECT')): raise PyleroLibException("The config files/ENV vars do not " "exist or are not of the correct " "format. Valid files are: {0}, {1} " "or {2}" .format(self.GLOBAL_CONFIG, self.LOCAL_CONFIG, self.CURDIR_CONFIG)) self.server_url = os.environ.get("POLARION_URL") or \ config.get(self.CONFIG_SECTION, "url") self.repo = os.environ.get("POLARION_REPO") or \ config.get(self.CONFIG_SECTION, "svn_repo") self.login = os.environ.get("POLARION_USERNAME") or \ config.get(self.CONFIG_SECTION, "user") self.pwd = os.environ.get("POLARION_PASSWORD") or \ config.get(self.CONFIG_SECTION, "password") try: self.timeout = os.environ.get("POLARION_TIMEOUT") or \ config.get(self.CONFIG_SECTION, "timeout") except: self.timeout = config.defaults['timeout'] try: self.timeout = int(self.timeout) except ValueError: raise PyleroLibException("The timeout value in the config" " file must be an integer") self.proj = os.environ.get("POLARION_PROJECT") or \ config.get(self.CONFIG_SECTION, "default_project") try: self.cert_path = os.environ.get("POLARION_CERT_PATH") or \ config.get(self.CONFIG_SECTION, "cert_path") except: self.cert_path = None if not (self.server_url and self.login and self.proj): raise PyleroLibException("The config files must contain " "valid values for: url, user, " "password and default_project")
def __init__(self, user_id=None, suds_object=None, uri=None): """User constructor. Args: user_id: when given, the object is populated with user's data suds_object: Polarion User object. When given, the object is populated by object data. uri: when given, the object is populated with user's data Notes: Either user_id, suds_object or uri can be passed in, not multiple References: Project.getUser Project.getUserByUri """ super(self.__class__, self).__init__(user_id, suds_object) # user_id will be null if called from the get_users class function if user_id or uri: if user_id: self._suds_object = \ self.session.project_client.service.getUser(user_id) elif uri: self._suds_object = self.session.project_client.service. \ getUserByUri(uri) if getattr(self._suds_object, "_unresolvable", True): raise PyleroLibException( "The user {0} was not found.".format(user_id))
def _verify_obj(self): # verifies if the object contains a suds object from the server by # checking if the uri field is populated. If no URI it didn't come from # the server if not getattr(self, "uri", None): raise PyleroLibException("There is no {0} loaded".format( self.__class__.__name__))
def __init__(self, project_id=None, doc_with_space=None, fields=None, uri=None, suds_object=None): """constructor for the Module object. Gets the module object from the Polarion server based on parameters passed in. Args: project_id: the project where the module is located doc_with_space: specific space/doc_name of the repository, required if project_id is given (Testing, Development, ...) fields: optional list of fields that should be contained in the returned object. uri: The Polarion specific uri of the module object suds_object: the WSDL Module object Returns: None References: Tracker.getModuleByLocation Tracker.getModuleByLocationWithFields Tracker.getModuleByUri Tracker.getModuleByUriWithFields """ super(self.__class__, self).__init__(suds_object=suds_object) # function names and parameter lists generated dynamically based on # parameters passed in. if doc_with_space or uri: function_name = "getModuleBy" parms = [] if doc_with_space: function_name += "Location" parms += [project_id, doc_with_space] elif uri: function_name += "Uri" parms.append(uri) if fields: function_name = "WithFields" parms.append(self._convert_obj_fields_to_polarion(fields)) self._suds_object = getattr(self.session.tracker_client.service, function_name)(*parms) if getattr(self._suds_object, "_unresolvable", True): raise PyleroLibException( "The Document {0} was not found.".format(doc_with_space or uri))
def _arr_obj_setter(self, val, field_name): """set function for attributes that reference an array of objects. It requires a single instance or list of either Pylero or WSDL objects or an empty list. An empty list erases the attribute value. Otherwise it sets the attribute the value passed in. Args: val: the value that the property is set to field_name: the field name of the Polarion object to set """ # TODO: Still needs to be fully tested. Looks like there are some bugs. csm = self._cls_suds_map[field_name] arr_inst = csm.get("arr_cls")() obj_inst = csm.get("cls")() # obj_attach = if not isinstance(val, (list, arr_inst.__class__, arr_inst._suds_object.__class__)): raise PyleroLibException( "{0}s must be a list of {1}").format( csm["field_name"], obj_inst.__class__.__name__) elif not val: setattr( self._suds_object, csm["field_name"], arr_inst._suds_object) elif isinstance(val, arr_inst._suds_object.__class__): setattr(self._suds_object, csm["field_name"], val) elif isinstance(val, arr_inst.__class__): setattr(self._suds_object, csm["field_name"], val._suds_object) else: if isinstance(val, list): # if str values are based in, try instantiating a class with # the vals and then using that list. Then continue processing if isinstance(val[0], basestring): val[0] = self._check_encode(val[0]) val = [csm["cls"](item) for item in val] if isinstance(val[0], obj_inst._suds_object.__class__): setattr(getattr(self._suds_object, csm["field_name"]), csm["inner_field_name"], val) else: setattr(self._suds_object, csm["field_name"], arr_inst._suds_object) for item in val: getattr(getattr(self._suds_object, csm["field_name"]), csm["inner_field_name"]).append( item._suds_object)
def __init__(self, project_id=None, suds_object=None, location=None, uri=None): """Project constructor. Args: project_id: when given, the object is populated with the Project data. suds_object: PolarionProject object. When given, the object is populated by object data. location: the location of the Polarion project uri: the uri that references the PolarionProject Notes: Either project_id or suds_object or location or uri can be passed in or none of them. If none of the identifying parameters are passed in an empty object is created References: Project.getProject Project.getProjectatLocation Project.getProjectByURI """ super(self.__class__, self).__init__(project_id, suds_object) if project_id: # if the project is already cached, make a deep copy and use it. # If not, get it and add it to the cache. project = self._cache["projects"].get(project_id) if project: self._suds_object = copy.deepcopy(project) else: self._suds_object = self.session.project_client.service. \ getProject(project_id) self._cache["projects"][project_id] = self._suds_object elif location: self._suds_object = self.session.project_client.service. \ getProjectatLocation(location) elif uri: self._suds_object = self.session.project_client.service. \ getProjectByURI(uri) if project_id or location or uri: if getattr(self._suds_object, "_unresolvable", True): raise PyleroLibException("The Project was not found.")
def get_user_avatar_url(self): """method get_user_avatar_url, returns a string with the relative URL of the user's avatar. Args: None Notes: Raises an error if the User is not populated. References: Project.getUserAvatarURL """ if self.user_id: return self.session.project_client.service.getUserAvatarURL( self.user_id) else: raise PyleroLibException("The user object is empty")
def _check_encode(self, val): """Validate @val is a UTF-8 because Polarion doesn't support not UTF-8 characters. The only use case that is not taken into account is when an attribute is set directly with a SUDS object. The users have no way of calling this function in this case. Args: val (string): the value that the property is being set to """ try: if not isinstance(val, type(u'')): val = val.decode('utf-8') # replace chr(160) with space return val.replace(u'\xa0', u' ') except UnicodeError as err: raise PyleroLibException( 'String must be UTF-8 encoded. The following error was ' 'raised when converting it to unicode: {0}'.format(err))
def update(self): """method update, updates Polarion with the User attributes Args: None Notes: Raises an error if the User is not populated. References: p.Project.updateUser """ if self.user_id: # self._map_to_suds() self.session.project_client.service.updateUser(self._suds_object) # CHECK for verification else: raise PyleroLibException("The user object is empty")
def _obj_setter(self, val, field_name): """set function for attributes that reference an object. It can accept a string, a Pylero object or a raw WSDL object. If a string is given, it is passed in to the object as its obj_id. Args: val: the value that the property is being set to field_name: the field name of the Polarion object to set """ csm = self._cls_suds_map[field_name] suds_field_name = csm["field_name"] enum_id = csm.get("enum_id") enum_override = csm.get("enum_override", []) sync_field = csm.get("sync_field") obj_cls = csm.get("cls") # deepcopy so that changes do not stick add_parms = copy.deepcopy(csm.get("additional_parms", {})) if isinstance(val, basestring): val = self._check_encode(val) if enum_id and val not in enum_override: self.check_valid_field_values( val, enum_id, {}, self._wi_type if hasattr(self, "_wi_type") else None) if not sync_field: sync_field = "_suds_object" if isinstance(val, basestring) or val is None: add_parms[obj_cls._id_field] = val obj = obj_cls(**add_parms) setattr(self._suds_object, suds_field_name, getattr(obj, sync_field)) elif isinstance(val, obj_cls): setattr(self._suds_object, suds_field_name, getattr(val, sync_field)) elif isinstance(val, obj_cls()._suds_object.__class__): obj = obj_cls() if sync_field in obj._cls_suds_map: suds_sync_field = obj._cls_suds_map[sync_field] # if sync_field is given, the attribute will be simple val = getattr(val, suds_sync_field) setattr(self._suds_object, suds_field_name, val) else: raise PyleroLibException("the value {0} is not a valid type". format(val))
def remove_plan_items(self, work_items): """Remove plan records to the plan. Args: items: list of work_item_ids Returns: None References: Planning.removePlanItems """ self._verify_obj() if work_items: if not isinstance(work_items, list): raise PyleroLibException( "work_items must be a list of _WorkItem objects") p_items = [] for item in work_items: wi = _WorkItem(self.project_id, work_item_id=item) p_items.append(wi.uri) self.session.planning_client.service.removePlanItems(self.uri, p_items)
def session(cls): if not cls.connected: cfg = Configuration() # if the password is not supplied in the config file, ask the user # for it if not cfg.pwd: cfg.pwd = getpass( "Password not in config file.\nEnter Password:"******"com.polarion.platform.security." \ "AuthenticationFailedException" \ in e.fault.faultstring: pwd = getpass("Invalid Password.\nEnter Password:"******"Unable to establish pylero session " "due to 3 incorrect login attempts") cls.session.default_project = cfg.proj cls.session.user_id = cfg.login cls.session.password = cfg.pwd cls.session.repo = cfg.repo # must use try/except instead of or because the config file # may return a non empty value, such as " " return cls.session
def create_work_item(self, parent_id, w_item): """create a work item in the current document Args: parent_id: The work_item_id of the parent _WorkItem wi: The Work Item object to create. Returns: The created _WorkItem References: Tracker.createWorkItemInModule """ self._verify_obj() if isinstance(w_item, _WorkItem): w_item.verify_required() suds_wi = w_item._suds_object else: raise PyleroLibException( "the w_item parameter must be a _WorkItem") if parent_id: parent_uri = _WorkItem(work_item_id=parent_id, project_id=self.project_id).uri else: doc_wis = self.get_work_items(None, False, None) if doc_wis: parent_uri = doc_wis[0].uri else: parent_uri = None wi_uri = self.session.tracker_client.service. \ createWorkItemInModule(self.uri, parent_uri, suds_wi) new_wi = w_item.__class__(uri=wi_uri) new_wi._changed_fields = w_item._changed_fields new_wi.update() new_wi = _WorkItem(uri=wi_uri) return new_wi
def _custom_setter(self, val, field_name): """Works with custom fields that has to keep track of values and what changed so that on update it can also update all the custom fields at the same time. Args: val: the value that the property is being set to field_name: the field name of the Polarion object to set """ csm = self._cls_suds_map[field_name] if field_name == "test_steps": if not val: self._changed_fields[csm["field_name"]] = None elif isinstance(val, csm["cls"]): self._changed_fields[csm["field_name"]] = val._suds_object elif isinstance(val, csm["cls"]()._suds_object.__class__): self._changed_fields[csm["field_name"]] = val else: raise PyleroLibException( "The value must be a {0}".format(csm["cls"].__name__)) # move the custom fields to within the object, otherwise each custom # field is a seperate SVN commit. testSteps, does not work unless it # is uploaded using the set_test_steps function. else: cust = self.custom_obj() cust.key = csm["field_name"] if val is None: cust.value = None elif (not csm.get("cls") or isinstance(val, basestring)) \ and not csm.get("is_array"): # if there is no cls specified, val can be a bool, int, ... # if val is a string, it may be used to instantiate the class if isinstance(val, basestring): val = self._check_encode(val) if csm.get("enum_id") and \ val not in csm.get("enum_override", []): # uses deepcopy, to not affect other instances of the class additional_parms = copy.deepcopy( csm.get("additional_parms", {})) self.check_valid_field_values(val, csm.get("enum_id"), additional_parms, csm.get("control")) cust.value = csm["cls"](val)._suds_object if csm.get("cls") \ else val elif csm.get("is_array"): if not isinstance(val, list): raise PyleroLibException("value must be a list") if csm.get("enum_id"): cust.value = csm["cls"]()._suds_object for i in val: if i not in csm.get("enum_override", []): # uses deepcopy, to not affect other instances # of the class additional_parms = copy.deepcopy( csm.get("additional_parms", {})) self.check_valid_field_values( i, csm.get("enum_id"), additional_parms, self._wi_type if hasattr(self,"_wi_type") else None) cust.value[0].append( csm["cls"]._cls_inner(i)._suds_object) elif isinstance(val, csm["cls"]): cust.value = val._suds_object elif isinstance(val, csm["cls"]()._suds_object.__class__): cust.value = val else: raise PyleroLibException( "The value must be of type {0}." .format(csm["cls"].__name__)) if "customFields" not in self._suds_object: self._suds_object.customFields = self.custom_array_obj() cf = self._suds_object.customFields[0] if cf: # check if the custom field already exists and modify it. match = [x for x in cf if x.key == csm["field_name"]] if match: match[0].value = cust.value else: cf.append(cust) self._custom_fields = cf else: self._custom_fields = [cust]
def __init__(self, obj_id=None, suds_object=None): # cls_suds_map must be available for some parameters on the class # level, but gets changed on the instance level and those changes # should not be accessible to other instances. This is the reason # for overwriting it as an instance attribute. self._cls_suds_map = copy.deepcopy(self._cls_suds_map) # _fix_circular_refs is a function that allows objects to contain # circular references by applying the reference only after the class # has been instantiated. Some objects contain references to themselves, # for example a parent attribute. if hasattr(self, "_fix_circular_refs"): self._fix_circular_refs() # if _id_field has not been set in the child class, the obj_id field # cannot be passed as a parameter. if obj_id and not self._id_field: raise PyleroLibException( "{0} only accepts a suds object, not an obj_id".format( self.__class__.__name)) if suds_object: self._suds_object = suds_object else: self._get_suds_object() # initialize all instance attributes as properties # check if the property already exists. If so, use existing. for key in list(self._cls_suds_map.keys()): if not hasattr(self.__class__, key): # require default values for lambda or it evaluates all # variables # at the end of function (self._cls_suds_map[key] was evaluated # as the last key value in the for loop for all defined lambdas # Property Builder, parses _cls_suds_map to build properties: # custom fields: # getter has parameters: # field_name # setter has parameters: # val: the value that the property is set to # field_name # array object fields: # getter has parameters: # field_name # setter has parameters: # val: the value that the property is set to # field_name # object fields: # getter has parameters: # field_name # setter has parameters: # val: the value that the property is set to # field_name # regular fields; # use getattr and setattr if isinstance(self._cls_suds_map[key], dict): if "is_custom" in self._cls_suds_map[key]: setattr(self.__class__, key, property( lambda self, field_name=key: self._custom_getter(field_name), lambda self, val, field_name=key: self._custom_setter(val, field_name))) elif "is_array" in self._cls_suds_map[key]: setattr(self.__class__, key, property( lambda self, field_name=key: self._arr_obj_getter(field_name), lambda self, val, field_name=key: self._arr_obj_setter(val, field_name))) else: setattr(self.__class__, key, property( lambda self, field_name=key: self._obj_getter(field_name), lambda self, val, field_name=key: self._obj_setter(val, field_name))) else: setattr(self.__class__, key, property( # if the attribute doesn't exist in the current object # return None lambda self, suds_key=self._cls_suds_map[key]: getattr(self._suds_object, suds_key, None), lambda self, value, suds_key=self._cls_suds_map[key]: self._regular_setter(value, suds_key))) # after all properties are defined set the id field to the value passed in. if obj_id is not None: setattr(self, self._id_field, obj_id)
def create(cls, project_id, space, document_name, document_title, allowed_wi_types, document_type, structure_link_role="parent", home_page_content=""): # There is no document object. # don't know what to do with the URI it returns. """class method create Creates a document or an old-style Module/Document in given location with given parameters. Args: project_id: project to create module in space: document space location with one component or None for default space document_name: Document name (required) document_title: Document title (required) allowed_wi_types: list of types, only one should be specified document_type: Type of document (required i.e testspecification). structure_link_role: required, role which defines the hierarchy of work items inside the Module, default: parent home_page_content: HTML markup for document home page, default "" Returns: None References: Tracker.createDocument """ if isinstance(allowed_wi_types, basestring): allowed_wi_types = [allowed_wi_types] awit = [EnumOptionId(item)._suds_object for item in allowed_wi_types] slr = EnumOptionId(structure_link_role)._suds_object try: uri = cls.session.tracker_client.service.createDocument( project_id, space, document_name, document_title, awit, slr, home_page_content) doc = Document(uri=uri) doc.type = document_type # for some reason, when in a tx (@tx_wrapper), the # returned doc does not include the home_page_content attribute # so it must be reset before the update. If it is not set, an # exception is raised: # "java.lang.IllegalArgumentException: Content can't be null" if not doc.home_page_content: doc.home_page_content = home_page_content doc.update() if not home_page_content: # create heading work item for each document wi_head = _WorkItem() wi_head.type = "heading" wi_head.title = document_title doc.create_work_item(None, wi_head) return doc except suds.WebFault as e: if "Invalid document on location Location" in e.fault.faultstring: raise PyleroLibException( "Document {0}/{1} already exists".format(space, document_name)) else: raise PyleroLibException(e.fault)