Skip to content

Commit 4ab36de

Browse files
authored
Merge pull request #9 from sw360/feat/have-add-flag-for-update-project
feat: `add_subprojects` parameter for update_project()
2 parents 121cc82 + 88dacbf commit 4ab36de

3 files changed

Lines changed: 231 additions & 9 deletions

File tree

ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* new method `update_project_release_relationship`.
55
* original get_health_status() endpoint URL has been restored by the SW360 team.
66
* fix: better check assumptions on returned data, see https://github.com/sw360/sw360python/issues/5.
7+
* `update_project` has a new parameter `add_subprojects` to only **add** the new
8+
sub-projects and not to overwrite all existing sub-projects.
79

810
## 1.1.0
911
* New method `duplicate_project` to create a copy of an existing project.

sw360/sw360_api.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ def create_new_project(self, name, project_type, visibility,
485485

486486
raise SW360Error(response, url)
487487

488-
def update_project(self, project, project_id):
488+
def update_project(self, project: dict, project_id: str, add_subprojects=False):
489489
"""Update an existing project
490490
491491
API endpoint: PATCH /projects
@@ -498,8 +498,21 @@ def update_project(self, project, project_id):
498498
:rtype: JSON SW360 result object
499499
:raises SW360Error: if there is a negative HTTP response
500500
"""
501-
# 2019-04-03: error 405 - method not allowed
501+
if not project_id:
502+
raise SW360Error(message="No project id provided!")
503+
502504
url = self.url + "resource/api/projects/" + project_id
505+
506+
if add_subprojects:
507+
current = self.get_project(project_id)
508+
if (current is not None and "linkedProjects" in current):
509+
for sp in current["linkedProjects"]:
510+
pid = self.get_id_from_href(sp["project"])
511+
if pid not in project["linkedProjects"]:
512+
nsp = {}
513+
nsp["projectRelationship"] = sp.get("relation", "CONTAINED")
514+
project["linkedProjects"][pid] = nsp
515+
503516
response = requests.patch(url, json=project, headers=self.api_headers)
504517

505518
if response.ok:
@@ -647,7 +660,8 @@ def duplicate_project(self, project_id: str, new_version: str):
647660

648661
raise SW360Error(response, url)
649662

650-
def update_project_release_relationship(self, project_id: str, release_id: str, new_state: str,
663+
def update_project_release_relationship(
664+
self, project_id: str, release_id: str, new_state: str,
651665
new_relation: str, comment: str):
652666
"""Update the relationship for a specific release of a project
653667
@@ -657,12 +671,12 @@ def update_project_release_relationship(self, project_id: str, release_id: str,
657671
:type project_id: string
658672
:param release_id: the id of the release to be requested
659673
:type release_id: string
660-
:param new_state: the new mainline state of the release, one of
674+
:param new_state: the new mainline state of the release, one of
661675
(OPEN, MAINLINE, SPECIFIC, PHASEOUT, DENIED)
662676
:type new_state: string
663-
:param new_relation: the new relation of the release, one of
664-
(CONTAINED, REFERRED, UNKNOWN, DYNAMICALLY_LINKED, STATICALLY_LINKED, SIDE_BY_SIDE, STANDALONE,
665-
INTERNAL_USE, OPTIONAL, TO_BE_REPLACED, CODE_SNIPPET)
677+
:param new_relation: the new relation of the release, one of
678+
(CONTAINED, REFERRED, UNKNOWN, DYNAMICALLY_LINKED, STATICALLY_LINKED, SIDE_BY_SIDE,
679+
STANDALONE, INTERNAL_USE, OPTIONAL, TO_BE_REPLACED, CODE_SNIPPET)
666680
:type new_relation: string
667681
:param comment: a comment
668682
:type comment: string

tests/test_sw360_projects.py

Lines changed: 208 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -------------------------------------------------------------------------------
2-
# (c) 2019-2021 Siemens AG
2+
# (c) 2019-2022 Siemens AG
33
# All Rights Reserved.
44
# Author: thomas.graf@siemens.com
55
#
@@ -572,7 +572,127 @@ def test_update_project(self):
572572
project["version"] = "9.99"
573573
project["projectType"] = "PRODUCT"
574574

575-
lib.update_project(project, "123")
575+
sub_projects = {}
576+
sub_proj = {}
577+
sub_proj["projectRelationship"] = "CONTAINED"
578+
579+
sub_projects["12345"] = sub_proj
580+
project["linkedProjects"] = sub_projects
581+
582+
lib.update_project(project, "123", False)
583+
584+
@responses.activate
585+
def test_update_project_sub_projects_no_add(self):
586+
lib = self.get_logged_in_lib()
587+
588+
responses.add(
589+
responses.GET,
590+
url=self.MYURL + "resource/api/projects/123",
591+
body='{"name": "My Testproject", "linkedProjects": []}',
592+
status=200,
593+
content_type="application/json",
594+
adding_headers={"Authorization": "Token " + self.MYTOKEN},
595+
)
596+
597+
responses.add(
598+
responses.PATCH,
599+
url=self.MYURL + "resource/api/projects/123",
600+
body="4",
601+
status=202,
602+
match=[
603+
responses.json_params_matcher({
604+
"name": "NewComponent",
605+
"version": "9.99",
606+
"projectType": "PRODUCT",
607+
"linkedProjects": {
608+
"12345": {"projectRelationship": "CONTAINED"}
609+
},
610+
})
611+
]
612+
)
613+
614+
project = {}
615+
project["name"] = "NewComponent"
616+
project["version"] = "9.99"
617+
project["projectType"] = "PRODUCT"
618+
619+
sub_projects = {}
620+
sub_proj = {}
621+
sub_proj["projectRelationship"] = "CONTAINED"
622+
623+
sub_projects["12345"] = sub_proj
624+
project["linkedProjects"] = sub_projects
625+
626+
lib.update_project(project, "123", True)
627+
628+
@responses.activate
629+
def test_update_project_sub_projects_with_add(self):
630+
lib = self.get_logged_in_lib()
631+
632+
responses.add(
633+
responses.GET,
634+
url=self.MYURL + "resource/api/projects/123",
635+
body='{"name": "My Testproject", ' +
636+
'"linkedProjects": [' +
637+
'{ "project": "998877" }' +
638+
']}',
639+
status=200,
640+
content_type="application/json",
641+
adding_headers={"Authorization": "Token " + self.MYTOKEN},
642+
)
643+
644+
responses.add(
645+
responses.PATCH,
646+
url=self.MYURL + "resource/api/projects/123",
647+
body="4",
648+
status=202,
649+
match=[
650+
responses.json_params_matcher({
651+
"name": "NewComponent",
652+
"version": "9.99",
653+
"projectType": "PRODUCT",
654+
"linkedProjects": {
655+
"12345": {"projectRelationship": "CONTAINED"},
656+
"998877": {"projectRelationship": "CONTAINED"}
657+
},
658+
})
659+
]
660+
)
661+
662+
project = {}
663+
project["name"] = "NewComponent"
664+
project["version"] = "9.99"
665+
project["projectType"] = "PRODUCT"
666+
667+
sub_projects = {}
668+
sub_proj = {}
669+
sub_proj["projectRelationship"] = "CONTAINED"
670+
671+
sub_projects["12345"] = sub_proj
672+
project["linkedProjects"] = sub_projects
673+
674+
lib.update_project(project, "123", True)
675+
676+
@responses.activate
677+
def test_update_project_no_id(self):
678+
lib = self.get_logged_in_lib()
679+
680+
responses.add(
681+
responses.PATCH,
682+
url=self.MYURL + "resource/api/projects/123",
683+
body="4",
684+
status=202,
685+
)
686+
687+
project = {}
688+
project["name"] = "NewComponent"
689+
project["version"] = "9.99"
690+
project["projectType"] = "PRODUCT"
691+
692+
with self.assertRaises(SW360Error) as context:
693+
lib.update_project(project, None)
694+
695+
self.assertEqual("No project id provided!", context.exception.message)
576696

577697
@responses.activate
578698
def test_update_project_failed(self):
@@ -822,6 +942,92 @@ def test_duplicate_project_failed(self):
822942
with self.assertRaises(SW360Error) as context:
823943
lib.duplicate_project("123", "42")
824944

945+
print(context.exception)
946+
self.assertEqual(404, context.exception.response.status_code)
947+
948+
@responses.activate
949+
def test_update_project_release_relationship_no_project_id(self):
950+
lib = self.get_logged_in_lib()
951+
952+
responses.add(
953+
responses.PATCH,
954+
url=self.MYURL + "resource/api/projects/123",
955+
body="4",
956+
status=202,
957+
)
958+
959+
project = {}
960+
project["name"] = "NewComponent"
961+
project["version"] = "9.99"
962+
project["projectType"] = "PRODUCT"
963+
964+
with self.assertRaises(SW360Error) as context:
965+
lib.update_project_release_relationship(None, "22", "state", "rel", "cmt")
966+
967+
self.assertEqual("No project id provided!", context.exception.message)
968+
969+
@responses.activate
970+
def test_update_project_release_relationship_no_release_id(self):
971+
lib = self.get_logged_in_lib()
972+
973+
responses.add(
974+
responses.PATCH,
975+
url=self.MYURL + "resource/api/projects/123",
976+
body="4",
977+
status=202,
978+
)
979+
980+
project = {}
981+
project["name"] = "NewComponent"
982+
project["version"] = "9.99"
983+
project["projectType"] = "PRODUCT"
984+
985+
with self.assertRaises(SW360Error) as context:
986+
lib.update_project_release_relationship("123", None, "state", "rel", "cmt")
987+
988+
self.assertEqual("No release id provided!", context.exception.message)
989+
990+
@responses.activate
991+
def test_update_project_release_relationship_failed(self):
992+
lib = self.get_logged_in_lib()
993+
994+
responses.add(
995+
responses.PATCH,
996+
url=self.MYURL + "resource/api/projects/123/release/9988",
997+
body="4",
998+
status=404,
999+
)
1000+
1001+
project = {}
1002+
project["name"] = "NewComponent"
1003+
project["version"] = "9.99"
1004+
project["projectType"] = "PRODUCT"
1005+
1006+
with self.assertRaises(SW360Error) as context:
1007+
lib.update_project_release_relationship("123", "9988", "state", "rel", "cmt")
1008+
1009+
self.assertEqual(404, context.exception.response.status_code)
1010+
1011+
@responses.activate
1012+
def test_update_project_release_relationship(self):
1013+
lib = self.get_logged_in_lib()
1014+
1015+
responses.add(
1016+
responses.PATCH,
1017+
url=self.MYURL + "resource/api/projects/123/release/987",
1018+
body="4",
1019+
status=202,
1020+
match=[
1021+
responses.json_params_matcher({
1022+
"releaseRelation": "STANDALONE",
1023+
"mainlineState": "SPECIFIC",
1024+
"comment": "mycomment"
1025+
})
1026+
]
1027+
)
1028+
1029+
lib.update_project_release_relationship("123", "987", "SPECIFIC", "STANDALONE", "mycomment")
1030+
8251031

8261032
if __name__ == "__main__":
8271033
unittest.main()

0 commit comments

Comments
 (0)