Skip to content

Commit 18fa1d3

Browse files
committed
Added example of creating folder dn_simple_createfolderandartifact.py, updated tests for 7.0.2>=iFix017, updated version to 0.18.0
1 parent 516b73f commit 18fa1d3

39 files changed

+2544
-2356
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.17.0
2+
current_version = 0.18.0
33
commit = True
44
tag = True
55

README.md

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

99
SPDX-License-Identifier: MIT
1010

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

1313

1414
Introduction

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.17.0'
12+
version = '0.18.0'
1313
license = 'MIT'
1414
author_name = 'Ian Barnard'
1515
author_mail = 'ian.barnard@uk.ibm.com'
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
##
2+
## © Copyright 2023- IBM Inc. All rights reserved
3+
# SPDX-License-Identifier: MIT
4+
##
5+
6+
# example of creating a folder and creating an artifact in it
7+
# provide on the commandline (each surrounded by " if it contains a space):
8+
# The name of the artifact type - case sensitive!
9+
# Some initial text to put in the artifact
10+
# The name of the folder where the artifact will be created - this is the path starting with / - case sensitive!
11+
# The name of the new folder
12+
13+
# 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/
14+
15+
# Also see section 2 of https://jazz.net/library/article/1197
16+
17+
# to create an artifact you have to find the creation factory
18+
# and find the 'instanceShape' for the type of artifact you want to create
19+
# then you POST some basic content compliant with the shape to the factory URL
20+
# this must also specify the folder where you want the artifact to be created - which means you need to find the folder URL
21+
# folders are found using a OSLC Query capability for folders - this returns one level at a time
22+
# sowill likely need a series of ueries to find an existing folder
23+
24+
import logging
25+
import os.path
26+
import sys
27+
import time
28+
29+
import lxml.etree as ET
30+
31+
import elmclient.server as elmserver
32+
import elmclient.utils as utils
33+
import elmclient.rdfxml as rdfxml
34+
35+
# setup logging - see levels in utils.py
36+
#loglevel = "INFO,INFO"
37+
loglevel = "TRACE,OFF"
38+
levels = [utils.loglevels.get(l,-1) for l in loglevel.split(",",1)]
39+
if len(levels)<2:
40+
# assert console logging level OFF if not provided
41+
levels.append(None)
42+
if -1 in levels:
43+
raise Exception( f'Logging level {loglevel} not valid - should be comma-separated one or two values from DEBUG, INFO, WARNING, ERROR, CRITICAL, OFF' )
44+
utils.setup_logging( filelevel=levels[0], consolelevel=levels[1] )
45+
46+
logger = logging.getLogger(__name__)
47+
48+
utils.log_commandline( os.path.basename(sys.argv[0]) )
49+
50+
jazzhost = 'https://jazz.ibm.com:9443'
51+
52+
username = 'ibm'
53+
password = 'ibm'
54+
55+
jtscontext = 'jts'
56+
rmcontext = 'rm'
57+
58+
# the project+compontent+config that will be updated
59+
proj = "rm_optout_p1"
60+
comp = proj
61+
conf = f"{comp} Initial Stream"
62+
63+
# caching control
64+
# 0=fully cached (but code below specifies queries aren't cached) - if you need to clear the cache, delet efolder .web_cache
65+
# 1=clear cache initially then continue with cache enabled
66+
# 2=clear cache and disable caching
67+
caching = 2
68+
69+
##################################################################################
70+
if __name__=="__main__":
71+
if len(sys.argv) != 5:
72+
print( 'A typical commandline might be: dn_simple_createfolderandartifact.py "Stakeholder Requirement" "My first stakefilder requirement" / newfoldername' )
73+
raise Exception( 'You must provide: The artifact type, the artifact text, and the folder path to create the artifact in - each surrounded by " if including spaces' )
74+
75+
newfoldername = sys.argv[4]
76+
77+
print( f"Attempting to create a '{sys.argv[1]}' in project '{proj}' in configuration {conf} in folder '{sys.argv[3]}'" )
78+
print( f"Using credentials user '{username}' password '{password}'")
79+
80+
# create our "server" which is how we connect to DOORS Next
81+
# 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)
82+
elmserver.setupproxy(jazzhost,proxyport=8888)
83+
theserver = elmserver.JazzTeamServer(jazzhost, username, password, verifysslcerts=False, jtsappstring=f"jts:{jtscontext}", appstring='rm', cachingcontrol=caching)
84+
85+
# create the RM application interface
86+
dnapp = theserver.find_app( f"rm:{rmcontext}", ok_to_create=True )
87+
88+
# open the project
89+
p = dnapp.find_project(proj)
90+
91+
# find the component
92+
c = p.find_local_component(comp)
93+
comp_u = c.project_uri
94+
print( f"{comp_u=}" )
95+
96+
# select the configuration
97+
config_u = c.get_local_config(conf)
98+
print( f"{config_u=}" )
99+
c.set_local_config(config_u)
100+
101+
# load the folder (using folder query capability)
102+
# NOTE this returns as soon as it finds a matching folder - i.e. doesn't load them all!
103+
# build the full new folder name so we can check if it already exists
104+
fullnewfoldername = sys.argv[3]
105+
if not fullnewfoldername.endswith( "/" ):
106+
fullnewfoldername += "/"
107+
fullnewfoldername += sys.argv[4]
108+
thefolder = c.load_folders(fullnewfoldername)
109+
110+
# check if the folder doesn't exist
111+
if thefolder is None:
112+
# have to create it!
113+
# get the parent
114+
thefolder = c.load_folders(sys.argv[3])
115+
if not thefolder:
116+
raise Exception( f"Parent folder '{sys.argv[3]}' doesn't exist!" )
117+
118+
# create the new folder
119+
folderfactory_u = c.reluri( "folders" )
120+
# this is a pretty nasty way of creating XML; would be much better to build it and let ET do the namespaces!
121+
# NOTE the rdf:about must be an empty string!
122+
newfolder_t = f"""<rdf:RDF
123+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
124+
xmlns:dcterms="http://purl.org/dc/terms/"
125+
xmlns:oslc="http://open-services.net/ns/core"
126+
xmlns:oslc_config="http://open-services.net/ns/config#"
127+
xmlns:nav="http://jazz.net/ns/rm/navigation#"
128+
xmlns:calm="http://jazz.net/xmlns/prod/jazz/calm/1.0/"
129+
>
130+
<nav:folder rdf:about="">
131+
<dcterms:title>{newfoldername}</dcterms:title>
132+
<dcterms:description>The description is optional.</dcterms:description>
133+
<nav:parent rdf:resource="{thefolder.folderuri}"/>
134+
</nav:folder>
135+
</rdf:RDF>"""
136+
137+
print( f"{newfolder_t=}" )
138+
newfolder_x = ET.fromstring( newfolder_t )
139+
140+
response = c.execute_post_rdf_xml( folderfactory_u, data=newfolder_x, params={'projectURL': p.reluri(f'process/project-areas/{p.iid}')}, headers={'vvc.configuration': config_u, 'Configuration-Context': None, }, intent=f"Create the new folder '{newfoldername}' in folder '{sys.argv[3]}'" )
141+
142+
thefolder_u = response.headers.get('Location')
143+
else:
144+
print( f"Folder URL = {thefolder.folderuri}" )
145+
thefolder_u = thefolder.folderuri
146+
# now the folder exists we can put the new artifact in it
147+
148+
# find the requirement creation factory
149+
factory_u, shapes = c.get_factory_uri("oslc_rm:Requirement", return_shapes=True)
150+
print( f"Factory URL = {factory_u}" )
151+
print( f"Shapes for this factory: {shapes}" )
152+
153+
# Find the type - read the shapes until find one matching the type name on the commandline
154+
# If you have two or more shapes with the same name this will only return the last matching one - the order is determined by the server and can vary - i.e. different shapes with the same name is a BAD idea!
155+
# Also shows all the shape names :-)
156+
theshape_u = None
157+
for shape_u in shapes:
158+
# retrieve the type
159+
shape_x = c.execute_get_rdf_xml( shape_u )
160+
# check its name
161+
shape_title = rdfxml.xmlrdf_get_resource_text( shape_x, ".//oslc:ResourceShape/dcterms:title" )
162+
print( f"{shape_title=}" )
163+
if shape_title == sys.argv[1]:
164+
theshape_u = shape_u
165+
print( f"Found!" )
166+
if theshape_u is None:
167+
raise Exception( f"Shape '{sys.argv[1]}' not found!" )
168+
169+
# text of the XML with basic content provided (this is based on example in section 2 of https://jazz.net/library/article/1197
170+
# If you want more complex and general purpose data such as custom attributes you probably need to use the instanceShape
171+
# this is a pretty nasty way of creating XML; would be much better to build it and let ET do the namespaces!
172+
thexml_t = f"""<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
173+
xmlns:dc="http://purl.org/dc/terms/"
174+
xmlns:public_rm_10="http://www.ibm.com/xmlns/rm/public/1.0/"
175+
xmlns:calm="http://jazz.net/xmlns/prod/jazz/calm/1.0/"
176+
xmlns:rm="http://www.ibm.com/xmlns/rdm/rdf/"
177+
xmlns:acp="http://jazz.net/ns/acp#"
178+
xmlns:rm_property="https://grarrc.ibm.com:9443/rm/types/"
179+
xmlns:oslc="http://open-services.net/ns/core#"
180+
xmlns:nav="http://jazz.net/ns/rm/navigation#"
181+
xmlns:oslc_rm="http://open-services.net/ns/rm#">
182+
<rdf:Description rdf:about="">
183+
<rdf:type rdf:resource="http://open-services.net/ns/rm#Requirement"/>
184+
<dc:description rdf:parseType="Literal">OSLC Creation Example</dc:description>
185+
<dc:title rdf:parseType="Literal">{sys.argv[2]}</dc:title>
186+
<oslc:instanceShape rdf:resource="{theshape_u}"/>
187+
<nav:parent rdf:resource="{thefolder_u}"/>
188+
</rdf:Description>
189+
</rdf:RDF>
190+
"""
191+
thexml_x = ET.fromstring( thexml_t )
192+
193+
# POST it to create the artifact
194+
response = c.execute_post_rdf_xml( factory_u, data=thexml_x, intent="Create the artifact" )
195+
print( f"POST result = {response.status_code}" )
196+
location = response.headers.get('Location')
197+
if response.status_code != 201:
198+
raise Exception( "POST failed!" )
199+
theartifact_u = location
200+
201+
# get the artifact so we can show its id
202+
theartifact_x = c.execute_get_rdf_xml( theartifact_u, intent="Retrieve the artifact so we can show its identifier" )
203+
204+
# show its ID
205+
theid = rdfxml.xml_find_element( theartifact_x, ".//dcterms:identifier" )
206+
print( f"Your new artifact has identifier {theid.text} URL {theartifact_u}" )

0 commit comments

Comments
 (0)