Skip to content

Commit

Permalink
0.19.0 - see what's new in README.md - RM-only: changeset create/deli…
Browse files Browse the repository at this point in the history
…ver, create folder, both with simple examples
  • Loading branch information
barny committed May 4, 2023
1 parent 49791f8 commit c6f81c0
Show file tree
Hide file tree
Showing 44 changed files with 4,146 additions and 3,070 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.18.0
current_version = 0.19.0
commit = True
tag = True

Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@

SPDX-License-Identifier: MIT

version="0.18.0"
version="0.19.0"

What's New?
===========

0.19.0 4-May-2023
* Deprecated RM load_folders() - use find_folder() instead.
* Added RM create_folder() - this doesn't require doing a forced reload of all folders, because it inserts the new folder into the internal info about folders
* Added example dn_simple_createfolderpath.py - allows exercising the create_folders() function
* Added rm-only implementation of create/deliver changeset NOTE these aren't fully finished, specifically hte delivery result may not be obvious(!)
* Added example dn_simple_typesystemimport_cs.py which shows creating/delivering a changeset around typesystem import

Introduction
============
Expand Down
2 changes: 1 addition & 1 deletion elmclient/__meta__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

app = 'elmoslcquery'
description = 'Commandline OSLC query for ELM'
version = '0.18.0'
version = '0.19.0'
license = 'MIT'
author_name = 'Ian Barnard'
author_mail = 'ian.barnard@uk.ibm.com'
Expand Down
10 changes: 10 additions & 0 deletions elmclient/_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,16 @@ def set_local_config(self, name_or_uri, global_config_uri=None):
# retrieve the services.xml in the current config!
self.services_xml = self.execute_get_rdf_xml(self.component_project.services_uri, intent="Retrieve project's services.xml")

# create a changeset in the current config (must be a stream)
def create_changeset( self, name ):
raise Exception( "Can't create a changeset on a project!" )

def discard_changeset( self ):
raise Exception( "Can't discard a changeset on a project!" )

def deliver_changeset( self ):
raise Exception( "Can't deliver a changeset on a project!" )

def _load_types(self,force=False):
logger.info( f"{self=}" )
raise Exception( "This must be implemented by the app-specific project class!" )
Expand Down
305 changes: 263 additions & 42 deletions elmclient/_rm.py

Large diffs are not rendered by default.

373 changes: 0 additions & 373 deletions elmclient/examples/basic/dn_basic_oslcquery.py

This file was deleted.

6 changes: 4 additions & 2 deletions elmclient/examples/dn_basic_oslcquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
# a self-contained example for 7.0.2/7.0.2SR1 not using elmclient - does all low-level HTTP GETs to discover to find the project+component_config and then do OSLC Query
# and then does a very basic save to CSV

# NOTE the very primitive authentication in this code only works with Liberty form authentication, i.e. NOT with JAS!

# You see all the fun of picking stuff out of the RDF XML
# It's quite possible this could be simpler or at least more readable using rdflib

# CONTRAST this with dn_simple_oslcquery.py which uses elmclient

# NOTE the authentication in this code only works with Liberty form authentication, i.e. NOT with JAS!

# this brutally minimal example has many limitations:
# only works for a query which doesn't involve custom attributes/links - because otherwise you have to find the URIs of these - hardcoded to get all resuls with no oslc.where
# only works for RM - it's hardcoded to rm
Expand Down Expand Up @@ -145,6 +145,8 @@ def getconfigtype( configuri ):
#
# low-level version NOT using elmclient AT ALL - everything including authentication for basic Liberty form auth is done here
#
# NOTE THIS ONLY WORKS FOR FORM-BASED login, i.e. doesn't work with JAS!
#

# generic HTTP GET with login if auth is needed
def get_with_optional_login( sess, url, *, params=None, headers=None, username="", password="", verify=False):
Expand Down
2 changes: 1 addition & 1 deletion elmclient/examples/dn_simple_createartifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@

# load the folder (using folder query capability)
# NOTE this returns as soon as it finds a matching folder - i.e. doesn't load them all!
thefolder = c.load_folders(sys.argv[3])
thefolder = c.find_folder(sys.argv[3])
if thefolder is None:
raise Exception( f"Folder '{sys.argv[3]}' not found!" )
print( f"Folder URL = {thefolder.folderuri}" )
Expand Down
4 changes: 2 additions & 2 deletions elmclient/examples/dn_simple_createfolderandartifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,13 @@
if not fullnewfoldername.endswith( "/" ):
fullnewfoldername += "/"
fullnewfoldername += sys.argv[4]
thefolder = c.load_folders(fullnewfoldername)
thefolder = c.find_folder(fullnewfoldername)

# check if the folder doesn't exist
if thefolder is None:
# have to create it!
# get the parent
thefolder = c.load_folders(sys.argv[3])
thefolder = c.find_folder(sys.argv[3])
if not thefolder:
raise Exception( f"Parent folder '{sys.argv[3]}' doesn't exist!" )

Expand Down
107 changes: 107 additions & 0 deletions elmclient/examples/dn_simple_createfolderpath.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
##
## © Copyright 2023- IBM Inc. All rights reserved
# SPDX-License-Identifier: MIT
##

# example of creating a folder path
# provide on the commandline (each surrounded by " if it contains a space) the apths of folders you want to create - paths MUST start with /

# For info about folder create API see https://rhnaranjo.wordpress.com/2012/06/25/folder-support-added-to-rrc-4-0-oslc-rm-api-implementation/

# Also see section 2 of https://jazz.net/library/article/1197

#
# folders are found using a OSLC Query capability for folders - this returns one level at a time
# so will likely need a series of queries to find an existing folder
# this is all implemented in load_fodlers()
# new create_folders() will create a folder path and update the loaded folders so a full reload isn't needed
#

import logging
import os.path
import sys
import time

import lxml.etree as ET

import elmclient.server as elmserver
import elmclient.utils as utils
import elmclient.rdfxml as rdfxml

# setup logging - see levels in utils.py
#loglevel = "INFO,INFO"
loglevel = "TRACE,OFF"
levels = [utils.loglevels.get(l,-1) for l in loglevel.split(",",1)]
if len(levels)<2:
# assert console logging level OFF if not provided
levels.append(None)
if -1 in levels:
raise Exception( f'Logging level {loglevel} not valid - should be comma-separated one or two values from DEBUG, INFO, WARNING, ERROR, CRITICAL, OFF' )
utils.setup_logging( filelevel=levels[0], consolelevel=levels[1] )

logger = logging.getLogger(__name__)

utils.log_commandline( os.path.basename(sys.argv[0]) )

jazzhost = 'https://jazz.ibm.com:9443'

username = 'ibm'
password = 'ibm'

jtscontext = 'jts'
rmcontext = 'rm'

# the project+compontent+config that will be updated
proj = "rm_optout_p1"
comp = proj
conf = f"{comp} Initial Stream"

# caching control
# 0=fully cached (but code below specifies queries aren't cached) - if you need to clear the cache, delet efolder .web_cache
# 1=clear cache initially then continue with cache enabled
# 2=clear cache and disable caching
caching = 2

##################################################################################
if __name__=="__main__":
if len(sys.argv) < 2:
raise Exception( 'Provide one or more space-separated paths on the commandline' )


print( f"Attempting to create paths '{sys.argv[1:]}' in project '{proj}' in configuration {conf}" )
print( f"Using credentials user '{username}' password '{password}'")

# create our "server" which is how we connect to DOORS Next
# first enable the proxy so if a proxy is running it can monitor the communication with server (this is ignored if proxy isn't running)
elmserver.setupproxy(jazzhost,proxyport=8888)
theserver = elmserver.JazzTeamServer(jazzhost, username, password, verifysslcerts=False, jtsappstring=f"jts:{jtscontext}", appstring='rm', cachingcontrol=caching)

# create the RM application interface
dnapp = theserver.find_app( f"rm:{rmcontext}", ok_to_create=True )

# open the project
p = dnapp.find_project(proj)

# find the component
c = p.find_local_component(comp)
comp_u = c.project_uri
print( f"{comp_u=}" )

# select the configuration
config_u = c.get_local_config(conf)
print( f"{config_u=}" )
c.set_local_config(config_u)

for path in sys.argv[1:]:

thefolder = c.find_folder(path)

# check if the path doesn't exist
if thefolder is None:
# have to create it!
# get the parent
thefolder = c.create_folder( path )
print( f"Folder '{path}' created uri is {thefolder.folderuri}" )
else:
print( f"Folder '{path}' already exists" )

2 changes: 2 additions & 0 deletions elmclient/examples/dn_simple_oslcquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#
# elmclient simple oslc query for hardcoded values
#
# by default this doesn't provide an oslc.where so the query will return all artifacts in the configuration
#

import sys
import os
Expand Down
151 changes: 151 additions & 0 deletions elmclient/examples/dn_simple_typesystemimport_cs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
##
## © Copyright 2021- IBM Inc. All rights reserved
# SPDX-License-Identifier: MIT
##

# extended example of using the type system import API, creating an automatically-named changeset, importing, delivering the CS
# also see https://jazz.net/wiki/bin/view/Main/DNGTypeImport

# the names for project/component/configs involved are hard-coded for simplicity


import csv
import datetime
import logging
import os.path
import sys
import time

import lxml.etree as ET
import tqdm

import elmclient.server as elmserver
import elmclient.utils as utils
import elmclient.rdfxml as rdfxml


# setup logging - see levels in utils.py - TRACE means you can use log2seq to get a html sequence diagram
#loglevel = "INFO,INFO"
loglevel = "TRACE,OFF"
levels = [utils.loglevels.get(l,-1) for l in loglevel.split(",",1)]
if len(levels)<2:
# assert console logging level OFF if not provided
levels.append(None)
if -1 in levels:
raise Exception( f'Logging level {loglevel} not valid - should be comma-separated one or two values from DEBUG, INFO, WARNING, ERROR, CRITICAL, OFF' )
utils.setup_logging( filelevel=levels[0], consolelevel=levels[1] )

logger = logging.getLogger(__name__)

utils.log_commandline( os.path.basename(sys.argv[0]) )

jazzhost = 'https://jazz.ibm.com:9443'

username = 'ibm'
password = 'ibm'

jtscontext = 'jts'
rmcontext = 'rm'

src_proj = "rm_optin_p1"
src_comp = "rm_optin_p1"
src_config = "rm_optin_p1 Initial Stream"
tgt_proj = "rm_optin_p2"
tgt_comp = "rm_optin_p2"
tgt_config = "rm_optin_p2 Initial Stream"

cs_name = "Typesystem import cs"

# caching control
# 0=fully cached (but code below specifies queries aren't cached) - if you need to clear the cache, delet efolder .web_cache
# 1=clear cache initially then continue with cache enabled
# 2=clear cache and disable caching
caching = 2


##################################################################################
if __name__=="__main__":

# first file handler:
datetimestamp = '{:%Y%m%d-%H%M%S}'.format(datetime.datetime.now())
csname = f"{cs_name} {datetimestamp}"
# create our "server" which is how we connect to DOORS Next
# first enable the proxy so if a proxy is running it can monitor the communication with server (this is ignored if proxy isn't running)
elmserver.setupproxy(jazzhost,proxyport=8888)
theserver = elmserver.JazzTeamServer(jazzhost, username, password, verifysslcerts=False, jtsappstring=f"jts:{jtscontext}", appstring='rm', cachingcontrol=caching)

# create the RM application interface
dnapp = theserver.find_app( f"rm:{rmcontext}", ok_to_create=True )

# open the source project
src_p = dnapp.find_project(src_proj)

# find the component
src_c = src_p.find_local_component(src_comp)
src_comp_u = src_c.project_uri
print( f"{src_comp_u=}" )

# select the configuration
src_config_u = src_c.get_local_config(src_config)
print( f"{src_config_u=}" )
src_c.set_local_config(src_config_u)


# open the target project
tgt_p = dnapp.find_project(tgt_proj)

# find the component
tgt_c = tgt_p.find_local_component(tgt_comp)
tgt_comp_u = tgt_c.project_uri
print( f"{tgt_comp_u=}" )

# select the configuration
tgt_config_u = tgt_c.get_local_config(tgt_config)
print( f"{tgt_config_u=}" )
tgt_c.set_local_config(tgt_config_u)

if tgt_config_u is None or src_config_u is None:
raise Exception( "Source or target config not found!" )

# create the changeset in the target config
cs_u = tgt_c.create_changeset( csname )
print( f"{cs_u=}" )

# switch to the changeset config for the import
tgt_c.set_local_config(cs_u)

# find the CreationFactory URI for type-delivery session
typeimport_u = tgt_c.get_factory_uri( resource_type="http://www.ibm.com/xmlns/rdm/types/TypeImportSession" )

# create the RDF body with the source and target configurations
content = f"""<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:types="http://www.ibm.com/xmlns/rdm/types/">
<types:TypeSystemCopySession>
<types:target rdf:resource="{tgt_config_u}"/>
<types:source rdf:resource="{src_config_u}"/>
</types:TypeSystemCopySession>
</rdf:RDF>"""

# initiate the delivery session - if successful will return 202 with a task tracker location
response = tgt_c.execute_post_rdf_xml( reluri=typeimport_u, data=content, cacheable=False, intent="Initiate typesystem import",action='start following task tracker' )
logger.debug( f" {response.status_code=} {response=}" )

# get the location
location = response.headers.get('Location')

# check for 202 and location
if response.status_code == 202 and location is not None:
# wait for the tracker to finished
result = tgt_c.wait_for_tracker( location, interval=1.0, progressbar=True, msg=f"Importing typesystem")
# TODO: success result is now the xml of the verdict
# result None is a success!
print( f"{result=}" )
if result is not None and type(result) is not str:
print( f"Failed Result={result}" )
else:
print( f"Success! {result=}" )
else:
raise Exception( "Typesystem import failed!" )

# now deliver the changeset
tgt_c.deliver_changeset()

Loading

0 comments on commit c6f81c0

Please sign in to comment.