Skip to content

Commit c6f81c0

Browse files
committed
0.19.0 - see what's new in README.md - RM-only: changeset create/deliver, create folder, both with simple examples
1 parent 49791f8 commit c6f81c0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+4146
-3070
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.18.0
2+
current_version = 0.19.0
33
commit = True
44
tag = True
55

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,17 @@
88

99
SPDX-License-Identifier: MIT
1010

11-
version="0.18.0"
11+
version="0.19.0"
1212

13+
What's New?
14+
===========
15+
16+
0.19.0 4-May-2023
17+
* Deprecated RM load_folders() - use find_folder() instead.
18+
* 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
19+
* Added example dn_simple_createfolderpath.py - allows exercising the create_folders() function
20+
* Added rm-only implementation of create/deliver changeset NOTE these aren't fully finished, specifically hte delivery result may not be obvious(!)
21+
* Added example dn_simple_typesystemimport_cs.py which shows creating/delivering a changeset around typesystem import
1322

1423
Introduction
1524
============

elmclient/__meta__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
app = 'elmoslcquery'
1111
description = 'Commandline OSLC query for ELM'
12-
version = '0.18.0'
12+
version = '0.19.0'
1313
license = 'MIT'
1414
author_name = 'Ian Barnard'
1515
author_mail = '[email protected]'

elmclient/_project.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,16 @@ def set_local_config(self, name_or_uri, global_config_uri=None):
241241
# retrieve the services.xml in the current config!
242242
self.services_xml = self.execute_get_rdf_xml(self.component_project.services_uri, intent="Retrieve project's services.xml")
243243

244+
# create a changeset in the current config (must be a stream)
245+
def create_changeset( self, name ):
246+
raise Exception( "Can't create a changeset on a project!" )
247+
248+
def discard_changeset( self ):
249+
raise Exception( "Can't discard a changeset on a project!" )
250+
251+
def deliver_changeset( self ):
252+
raise Exception( "Can't deliver a changeset on a project!" )
253+
244254
def _load_types(self,force=False):
245255
logger.info( f"{self=}" )
246256
raise Exception( "This must be implemented by the app-specific project class!" )

elmclient/_rm.py

Lines changed: 263 additions & 42 deletions
Large diffs are not rendered by default.

elmclient/examples/basic/dn_basic_oslcquery.py

Lines changed: 0 additions & 373 deletions
This file was deleted.

elmclient/examples/dn_basic_oslcquery.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
# 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
77
# and then does a very basic save to CSV
88

9+
# NOTE the very primitive authentication in this code only works with Liberty form authentication, i.e. NOT with JAS!
10+
911
# You see all the fun of picking stuff out of the RDF XML
1012
# It's quite possible this could be simpler or at least more readable using rdflib
1113

1214
# CONTRAST this with dn_simple_oslcquery.py which uses elmclient
1315

14-
# NOTE the authentication in this code only works with Liberty form authentication, i.e. NOT with JAS!
15-
1616
# this brutally minimal example has many limitations:
1717
# 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
1818
# only works for RM - it's hardcoded to rm
@@ -145,6 +145,8 @@ def getconfigtype( configuri ):
145145
#
146146
# low-level version NOT using elmclient AT ALL - everything including authentication for basic Liberty form auth is done here
147147
#
148+
# NOTE THIS ONLY WORKS FOR FORM-BASED login, i.e. doesn't work with JAS!
149+
#
148150

149151
# generic HTTP GET with login if auth is needed
150152
def get_with_optional_login( sess, url, *, params=None, headers=None, username="", password="", verify=False):

elmclient/examples/dn_simple_createartifact.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595

9696
# load the folder (using folder query capability)
9797
# NOTE this returns as soon as it finds a matching folder - i.e. doesn't load them all!
98-
thefolder = c.load_folders(sys.argv[3])
98+
thefolder = c.find_folder(sys.argv[3])
9999
if thefolder is None:
100100
raise Exception( f"Folder '{sys.argv[3]}' not found!" )
101101
print( f"Folder URL = {thefolder.folderuri}" )

elmclient/examples/dn_simple_createfolderandartifact.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,13 @@
105105
if not fullnewfoldername.endswith( "/" ):
106106
fullnewfoldername += "/"
107107
fullnewfoldername += sys.argv[4]
108-
thefolder = c.load_folders(fullnewfoldername)
108+
thefolder = c.find_folder(fullnewfoldername)
109109

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

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
##
2+
## © Copyright 2023- IBM Inc. All rights reserved
3+
# SPDX-License-Identifier: MIT
4+
##
5+
6+
# example of creating a folder path
7+
# provide on the commandline (each surrounded by " if it contains a space) the apths of folders you want to create - paths MUST start with /
8+
9+
# 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/
10+
11+
# Also see section 2 of https://jazz.net/library/article/1197
12+
13+
#
14+
# folders are found using a OSLC Query capability for folders - this returns one level at a time
15+
# so will likely need a series of queries to find an existing folder
16+
# this is all implemented in load_fodlers()
17+
# new create_folders() will create a folder path and update the loaded folders so a full reload isn't needed
18+
#
19+
20+
import logging
21+
import os.path
22+
import sys
23+
import time
24+
25+
import lxml.etree as ET
26+
27+
import elmclient.server as elmserver
28+
import elmclient.utils as utils
29+
import elmclient.rdfxml as rdfxml
30+
31+
# setup logging - see levels in utils.py
32+
#loglevel = "INFO,INFO"
33+
loglevel = "TRACE,OFF"
34+
levels = [utils.loglevels.get(l,-1) for l in loglevel.split(",",1)]
35+
if len(levels)<2:
36+
# assert console logging level OFF if not provided
37+
levels.append(None)
38+
if -1 in levels:
39+
raise Exception( f'Logging level {loglevel} not valid - should be comma-separated one or two values from DEBUG, INFO, WARNING, ERROR, CRITICAL, OFF' )
40+
utils.setup_logging( filelevel=levels[0], consolelevel=levels[1] )
41+
42+
logger = logging.getLogger(__name__)
43+
44+
utils.log_commandline( os.path.basename(sys.argv[0]) )
45+
46+
jazzhost = 'https://jazz.ibm.com:9443'
47+
48+
username = 'ibm'
49+
password = 'ibm'
50+
51+
jtscontext = 'jts'
52+
rmcontext = 'rm'
53+
54+
# the project+compontent+config that will be updated
55+
proj = "rm_optout_p1"
56+
comp = proj
57+
conf = f"{comp} Initial Stream"
58+
59+
# caching control
60+
# 0=fully cached (but code below specifies queries aren't cached) - if you need to clear the cache, delet efolder .web_cache
61+
# 1=clear cache initially then continue with cache enabled
62+
# 2=clear cache and disable caching
63+
caching = 2
64+
65+
##################################################################################
66+
if __name__=="__main__":
67+
if len(sys.argv) < 2:
68+
raise Exception( 'Provide one or more space-separated paths on the commandline' )
69+
70+
71+
print( f"Attempting to create paths '{sys.argv[1:]}' in project '{proj}' in configuration {conf}" )
72+
print( f"Using credentials user '{username}' password '{password}'")
73+
74+
# create our "server" which is how we connect to DOORS Next
75+
# 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)
76+
elmserver.setupproxy(jazzhost,proxyport=8888)
77+
theserver = elmserver.JazzTeamServer(jazzhost, username, password, verifysslcerts=False, jtsappstring=f"jts:{jtscontext}", appstring='rm', cachingcontrol=caching)
78+
79+
# create the RM application interface
80+
dnapp = theserver.find_app( f"rm:{rmcontext}", ok_to_create=True )
81+
82+
# open the project
83+
p = dnapp.find_project(proj)
84+
85+
# find the component
86+
c = p.find_local_component(comp)
87+
comp_u = c.project_uri
88+
print( f"{comp_u=}" )
89+
90+
# select the configuration
91+
config_u = c.get_local_config(conf)
92+
print( f"{config_u=}" )
93+
c.set_local_config(config_u)
94+
95+
for path in sys.argv[1:]:
96+
97+
thefolder = c.find_folder(path)
98+
99+
# check if the path doesn't exist
100+
if thefolder is None:
101+
# have to create it!
102+
# get the parent
103+
thefolder = c.create_folder( path )
104+
print( f"Folder '{path}' created uri is {thefolder.folderuri}" )
105+
else:
106+
print( f"Folder '{path}' already exists" )
107+

elmclient/examples/dn_simple_oslcquery.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#
88
# elmclient simple oslc query for hardcoded values
99
#
10+
# by default this doesn't provide an oslc.where so the query will return all artifacts in the configuration
11+
#
1012

1113
import sys
1214
import os
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
##
2+
## © Copyright 2021- IBM Inc. All rights reserved
3+
# SPDX-License-Identifier: MIT
4+
##
5+
6+
# extended example of using the type system import API, creating an automatically-named changeset, importing, delivering the CS
7+
# also see https://jazz.net/wiki/bin/view/Main/DNGTypeImport
8+
9+
# the names for project/component/configs involved are hard-coded for simplicity
10+
11+
12+
import csv
13+
import datetime
14+
import logging
15+
import os.path
16+
import sys
17+
import time
18+
19+
import lxml.etree as ET
20+
import tqdm
21+
22+
import elmclient.server as elmserver
23+
import elmclient.utils as utils
24+
import elmclient.rdfxml as rdfxml
25+
26+
27+
# setup logging - see levels in utils.py - TRACE means you can use log2seq to get a html sequence diagram
28+
#loglevel = "INFO,INFO"
29+
loglevel = "TRACE,OFF"
30+
levels = [utils.loglevels.get(l,-1) for l in loglevel.split(",",1)]
31+
if len(levels)<2:
32+
# assert console logging level OFF if not provided
33+
levels.append(None)
34+
if -1 in levels:
35+
raise Exception( f'Logging level {loglevel} not valid - should be comma-separated one or two values from DEBUG, INFO, WARNING, ERROR, CRITICAL, OFF' )
36+
utils.setup_logging( filelevel=levels[0], consolelevel=levels[1] )
37+
38+
logger = logging.getLogger(__name__)
39+
40+
utils.log_commandline( os.path.basename(sys.argv[0]) )
41+
42+
jazzhost = 'https://jazz.ibm.com:9443'
43+
44+
username = 'ibm'
45+
password = 'ibm'
46+
47+
jtscontext = 'jts'
48+
rmcontext = 'rm'
49+
50+
src_proj = "rm_optin_p1"
51+
src_comp = "rm_optin_p1"
52+
src_config = "rm_optin_p1 Initial Stream"
53+
tgt_proj = "rm_optin_p2"
54+
tgt_comp = "rm_optin_p2"
55+
tgt_config = "rm_optin_p2 Initial Stream"
56+
57+
cs_name = "Typesystem import cs"
58+
59+
# caching control
60+
# 0=fully cached (but code below specifies queries aren't cached) - if you need to clear the cache, delet efolder .web_cache
61+
# 1=clear cache initially then continue with cache enabled
62+
# 2=clear cache and disable caching
63+
caching = 2
64+
65+
66+
##################################################################################
67+
if __name__=="__main__":
68+
69+
# first file handler:
70+
datetimestamp = '{:%Y%m%d-%H%M%S}'.format(datetime.datetime.now())
71+
csname = f"{cs_name} {datetimestamp}"
72+
# create our "server" which is how we connect to DOORS Next
73+
# 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)
74+
elmserver.setupproxy(jazzhost,proxyport=8888)
75+
theserver = elmserver.JazzTeamServer(jazzhost, username, password, verifysslcerts=False, jtsappstring=f"jts:{jtscontext}", appstring='rm', cachingcontrol=caching)
76+
77+
# create the RM application interface
78+
dnapp = theserver.find_app( f"rm:{rmcontext}", ok_to_create=True )
79+
80+
# open the source project
81+
src_p = dnapp.find_project(src_proj)
82+
83+
# find the component
84+
src_c = src_p.find_local_component(src_comp)
85+
src_comp_u = src_c.project_uri
86+
print( f"{src_comp_u=}" )
87+
88+
# select the configuration
89+
src_config_u = src_c.get_local_config(src_config)
90+
print( f"{src_config_u=}" )
91+
src_c.set_local_config(src_config_u)
92+
93+
94+
# open the target project
95+
tgt_p = dnapp.find_project(tgt_proj)
96+
97+
# find the component
98+
tgt_c = tgt_p.find_local_component(tgt_comp)
99+
tgt_comp_u = tgt_c.project_uri
100+
print( f"{tgt_comp_u=}" )
101+
102+
# select the configuration
103+
tgt_config_u = tgt_c.get_local_config(tgt_config)
104+
print( f"{tgt_config_u=}" )
105+
tgt_c.set_local_config(tgt_config_u)
106+
107+
if tgt_config_u is None or src_config_u is None:
108+
raise Exception( "Source or target config not found!" )
109+
110+
# create the changeset in the target config
111+
cs_u = tgt_c.create_changeset( csname )
112+
print( f"{cs_u=}" )
113+
114+
# switch to the changeset config for the import
115+
tgt_c.set_local_config(cs_u)
116+
117+
# find the CreationFactory URI for type-delivery session
118+
typeimport_u = tgt_c.get_factory_uri( resource_type="http://www.ibm.com/xmlns/rdm/types/TypeImportSession" )
119+
120+
# create the RDF body with the source and target configurations
121+
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/">
122+
<types:TypeSystemCopySession>
123+
<types:target rdf:resource="{tgt_config_u}"/>
124+
<types:source rdf:resource="{src_config_u}"/>
125+
</types:TypeSystemCopySession>
126+
</rdf:RDF>"""
127+
128+
# initiate the delivery session - if successful will return 202 with a task tracker location
129+
response = tgt_c.execute_post_rdf_xml( reluri=typeimport_u, data=content, cacheable=False, intent="Initiate typesystem import",action='start following task tracker' )
130+
logger.debug( f" {response.status_code=} {response=}" )
131+
132+
# get the location
133+
location = response.headers.get('Location')
134+
135+
# check for 202 and location
136+
if response.status_code == 202 and location is not None:
137+
# wait for the tracker to finished
138+
result = tgt_c.wait_for_tracker( location, interval=1.0, progressbar=True, msg=f"Importing typesystem")
139+
# TODO: success result is now the xml of the verdict
140+
# result None is a success!
141+
print( f"{result=}" )
142+
if result is not None and type(result) is not str:
143+
print( f"Failed Result={result}" )
144+
else:
145+
print( f"Success! {result=}" )
146+
else:
147+
raise Exception( "Typesystem import failed!" )
148+
149+
# now deliver the changeset
150+
tgt_c.deliver_changeset()
151+

0 commit comments

Comments
 (0)