From 39d5d48e2ac442d82ebc041c1f8175aa2b48f16c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9lanie=20Bats?= Date: Fri, 3 Jan 2025 17:46:00 +0100 Subject: [PATCH] [doc] Contribute API cookbook recipes in Python MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mélanie Bats --- .../modules/ROOT/pages/integration.adoc | 2 +- .../attachments/sirius-web-openapi.json | 2 +- .../examples/create_project.py | 50 ++++++ .../developer-guide/examples/fetch_commits.py | 43 +++++ .../examples/fetch_elements.py | 34 ++++ .../examples/fetch_projects.py | 26 +++ .../examples/get_feature_members.py | 46 +++++ .../examples/get_owned_elements.py | 66 ++++++++ .../developer-guide/examples/init_api.py | 29 ++++ .../developer-guide/pages/api-cookbook.adoc | 63 +++++++ .../developer-guide/pages/api-details.adoc | 70 ++++++++ .../modules/developer-guide/pages/api.adoc | 28 ++++ .../pages/element_owned_elements_recipe.adoc | 80 +++++++++ .../modules/developer-guide/pages/index.adoc | 8 +- .../project_commit_branch_tag_recipe.adoc | 158 ++++++++++++++++++ .../modules/developer-guide/partials/nav.adoc | 5 +- .../assets/images/batmobile-project.png | Bin 0 -> 60490 bytes .../modules/user-manual/pages/faq/faq.adoc | 2 +- .../user-manual/pages/integration/api.adoc | 72 -------- .../pages/integration/developer-guide.adoc | 74 -------- .../user-manual/pages/key-features.adoc | 2 +- .../pages/release-notes/2024.11.0.adoc | 8 +- .../user-manual/partials/nav-integration.adoc | 4 +- 23 files changed, 714 insertions(+), 158 deletions(-) rename doc/content/modules/{user-manual => developer-guide}/assets/attachments/sirius-web-openapi.json (99%) create mode 100644 doc/content/modules/developer-guide/examples/create_project.py create mode 100644 doc/content/modules/developer-guide/examples/fetch_commits.py create mode 100644 doc/content/modules/developer-guide/examples/fetch_elements.py create mode 100644 doc/content/modules/developer-guide/examples/fetch_projects.py create mode 100644 doc/content/modules/developer-guide/examples/get_feature_members.py create mode 100644 doc/content/modules/developer-guide/examples/get_owned_elements.py create mode 100644 doc/content/modules/developer-guide/examples/init_api.py create mode 100644 doc/content/modules/developer-guide/pages/api-cookbook.adoc create mode 100644 doc/content/modules/developer-guide/pages/api-details.adoc create mode 100644 doc/content/modules/developer-guide/pages/api.adoc create mode 100644 doc/content/modules/developer-guide/pages/element_owned_elements_recipe.adoc create mode 100644 doc/content/modules/developer-guide/pages/project_commit_branch_tag_recipe.adoc create mode 100644 doc/content/modules/user-manual/assets/images/batmobile-project.png delete mode 100644 doc/content/modules/user-manual/pages/integration/api.adoc delete mode 100644 doc/content/modules/user-manual/pages/integration/developer-guide.adoc diff --git a/doc/content/modules/ROOT/pages/integration.adoc b/doc/content/modules/ROOT/pages/integration.adoc index caa75732..33a11164 100644 --- a/doc/content/modules/ROOT/pages/integration.adoc +++ b/doc/content/modules/ROOT/pages/integration.adoc @@ -18,7 +18,7 @@ a|image::tap_app.svg[xref=user-manual:key-features.adoc] Standard APIs support for seamless connections. -xref:user-manual:integration/api.adoc[Delve into] +xref:developer-guide:api.adoc[Delve into] a|image::globe.svg[xref=user-manual:what-is.adoc] diff --git a/doc/content/modules/user-manual/assets/attachments/sirius-web-openapi.json b/doc/content/modules/developer-guide/assets/attachments/sirius-web-openapi.json similarity index 99% rename from doc/content/modules/user-manual/assets/attachments/sirius-web-openapi.json rename to doc/content/modules/developer-guide/assets/attachments/sirius-web-openapi.json index 4f31af45..dbf90244 100644 --- a/doc/content/modules/user-manual/assets/attachments/sirius-web-openapi.json +++ b/doc/content/modules/developer-guide/assets/attachments/sirius-web-openapi.json @@ -1 +1 @@ -{"openapi":"3.1.0","info":{"title":"OpenAPI definition","version":"v0"},"paths":{"/api/rest/projects/{projectId}":{"get":{"tags":["project-rest-controller"],"description":"Get project with the given id (projectId).","operationId":"getProjectById","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}}}},"put":{"tags":["project-rest-controller"],"description":"Update the project with the given id (projectId).","operationId":"updateProject","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"query","required":false,"schema":{"type":"string"}},{"name":"description","in":"query","required":false,"schema":{"type":"string"}},{"name":"branch","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Branch"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}}}},"delete":{"tags":["project-rest-controller"],"description":"Delete the project with the given id (projectId).","operationId":"deleteProject","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}}}}},"/api/rest/projects":{"get":{"tags":["project-rest-controller"],"description":"Get all projects.","operationId":"getProjects","parameters":[{"name":"page[size]","in":"query","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"page[after]","in":"query","required":false,"schema":{"type":"string"}},{"name":"page[before]","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}}}}}},"post":{"tags":["project-rest-controller"],"description":"Create a new project with the given name and description (optional).","operationId":"createProject","parameters":[{"name":"name","in":"query","required":true,"schema":{"type":"string"}},{"name":"description","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}}}}},"/api/rest/projects/{projectId}/commits":{"get":{"tags":["commit-rest-controller"],"description":"Get all the commits in the given project.","operationId":"getCommits","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Commit"}}}}}}},"post":{"tags":["commit-rest-controller"],"description":"Create a new commit with the given change (collection\nof DataVersion records) in the given branch of the\nproject. If the branch is not specified, the default branch\nof the project is used. Commit.change should include\nthe following for each Data object that needs to be\ncreated, updated, or deleted in the new commit. (1)\nCreating Data - Commit.change should include a\nDataVersion record with DataVersion.payload\npopulated with the Data being created.\nDataVersion.identity is not provided, thereby indicating\nthat a new DataIdentity needs to be created in the new\ncommit. (2) Updating Data - Commit.change should\ninclude a DataVersion record with DataVersion.payload\npopulated with the updated Data. DataVersion.identity\nshould be populated with the DataIdentity for which a\nnew DataVersion record will be created in the new\ncommit. (3) Deleting Data - Commit.change should\ninclude a DataVersion record with DataVersion.payload\nnot provided, thereby indicating deletion of DataIdentity\nin the new commit. DataVersion.identity should be\npopulated with the DataIdentity that will be deleted in\nthe new commit. When a DataIdentity is deleted in a\ncommit, all its versions (DataVersion) are also deleted,\nand any references from other DataIdentity are also\nremoved to maintain data integrity. In addition, for\nElement Data (KerML), deletion of an Element must\nalso result in deletion of incoming Relationships. When\nElement Data (KerML) is created or updated, derived\nproperties must be computed or verified if the API\nprovider claims Derived Property Conformance.\n","operationId":"createCommit","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"branchId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Commit"}}}}}}},"/api/rest/projects/{projectId}/commits/{commitId}":{"get":{"tags":["commit-rest-controller"],"description":"Get the commit with the given id (commitId) in the given project.","operationId":"getCommitById","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Commit"}}}},"404":{"description":"Not Found"}}}},"/api/rest/projects/{projectId}/commits/{commitId}/roots":{"get":{"tags":["object-rest-controller"],"description":"Get all the root elements in the given project at the given commit.","operationId":"getRootElements","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"example":[{"@id":"9f2e43d4-2f7e-49f1-816a-a4e0d3d1f370","attribute1":"hello"},{"@id":"bab1f7be-82e0-4d14-bc60-b12a60c46f2f","attribute1":"bye"}]}}}}}},"/api/rest/projects/{projectId}/commits/{commitId}/elements":{"get":{"tags":["object-rest-controller"],"description":"Get all the elements in a given project at the given commit.","operationId":"getElements","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"example":[{"@id":"9f2e43d4-2f7e-49f1-816a-a4e0d3d1f370","attribute1":"hello"},{"@id":"bab1f7be-82e0-4d14-bc60-b12a60c46f2f","attribute1":"bye"}]}}}}}},"/api/rest/projects/{projectId}/commits/{commitId}/elements/{relatedElementId}/relationships":{"get":{"tags":["object-rest-controller"],"description":"Get relationships that are incoming, outgoing, or both relative to the given related element.","operationId":"getRelationshipsByRelatedElement","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"relatedElementId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"direction","in":"query","required":false,"schema":{"type":"string","enum":["IN","OUT","BOTH"]}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"example":[{"@id":"9f2e43d4-2f7e-49f1-816a-a4e0d3d1f370","attribute1":"hello"},{"@id":"bab1f7be-82e0-4d14-bc60-b12a60c46f2f","attribute1":"bye"}]}}}}}},"/api/rest/projects/{projectId}/commits/{commitId}/elements/{elementId}":{"get":{"tags":["object-rest-controller"],"description":"Get element with the given id (elementId) in the given project at the given commit.","operationId":"getElementById","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"elementId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"example":{"@id":"9f2e43d4-2f7e-49f1-816a-a4e0d3d1f370","attribute1":"hello"}}}}}}},"/api/rest/projects/{projectId}/commits/{commitId}/changes":{"get":{"tags":["commit-rest-controller"],"description":"Get the change in the given commit of the given project.","operationId":"getCommitChange","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"changeTypes","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","enum":["CREATED","UPDATED","DELETED"]}}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DataVersion"}}}}},"404":{"description":"Not Found"}}}},"/api/rest/projects/{projectId}/commits/{commitId}/changes/{changeId}":{"get":{"tags":["commit-rest-controller"],"description":"Get the change with the given id (changeId) in the given commit of the given project. The changeId is the id of the DataVersion that changed in the commit.","operationId":"getCommitChangeById","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"changeId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataVersion"}}}},"404":{"description":"Not Found"}}}}},"components":{"schemas":{"Branch":{"type":"object","description":"Branch is an indirect subclass of Record (via CommitReference) that represents an independent line of development in a Project. A Project can have 1 or more Branches. When a Project is created, a default Branch is also created. The default Branch of a Project can be changed, and a Project can have only 1 default Branch.","properties":{"@id":{"type":"string","format":"uuid","description":"The UUID assigned to the record"},"@type":{"type":"string","description":"Branch"},"created":{"type":"string","format":"date-time","description":"The timestamp at which the Branch was created, in ISO8601DateTime format"},"head":{"$ref":"#/components/schemas/Identified","description":"The Commit to which the Branch is currently pointing. It represents the latest state of the Project on the given Branch"},"name":{"type":"string","description":"The name of the Branch"},"owningProject":{"$ref":"#/components/schemas/Identified","description":"The Project that owns the given Branch"},"referencedCommit":{"$ref":"#/components/schemas/Identified","description":"The Commit referenced by the Branch"}},"required":["@id","@type","created","name","owningProject","referencedCommit"]},"Identified":{"type":"object","description":"Identified represents an Object through its ID and type.","properties":{"@id":{"type":"string"}}},"Project":{"type":"object","description":"Project is a subclass of Record that represents a container for other Records and an entry point for version management and data navigation.","properties":{"@id":{"type":"string","description":"The UUID assigned to the record"},"@type":{"type":"string","description":"Project"},"created":{"type":"string","format":"date-time","description":"The creation timestamp for the Project, in ISO8601DateTime format"},"defaultBranch":{"$ref":"#/components/schemas/Identified","description":"The default branch in the Project and a subset of branches"},"description":{"type":"string","description":"The statement that provides details about the record"},"name":{"type":"string","description":"The name of the Project"}},"required":["@id","@type","created","defaultBranch","name"]},"Commit":{"type":"object","description":"Commit is a subclass of Record that represents the changes made to a Project at a specific point in time in its lifecycle, such as the creation, update, or deletion of data in a Project. A Project has 0 or more Commits.","properties":{"@id":{"type":"string","format":"uuid","description":"The UUID assigned to the record"},"@type":{"type":"string","description":"Commit"},"created":{"type":"string","format":"date-time","description":"The timestamp at which the Commit was created, in ISO8601DateTime format"},"description":{"type":"string","description":"The statement that provides details about the record"},"owningProject":{"$ref":"#/components/schemas/Identified","description":"The Project that owns the Commit"},"previousCommits":{"type":"array","description":"The set of immediately preceding Commits","items":{"$ref":"#/components/schemas/Identified"}}},"required":["@id","@type","created","owningProject","previousCommits"]},"DataIdentity":{"type":"object","description":"DataIdentity is a subclass of Record that represents a unique, version-independent representation of Data through its lifecycle. A DataIdentity is associated with 1 or more DataVersion records that represent different versions of the same Data.","properties":{"@id":{"type":"string","format":"uuid","description":"The UUID assigned to the record"},"@type":{"type":"string","description":"DataIdentity"}},"required":["@id","@type"]},"DataVersion":{"type":"object","description":"DataVersion is a subclass of Record that represents Data at a specific version in its lifecycle. A DataVersion record is associated with only one DataIdentity record. DataVersion serves as a wrapper for Data (payload) in the context of a Commit in a Project.","properties":{"@id":{"type":"string","format":"uuid","description":"The UUID assigned to the record"},"@type":{"type":"string","description":"DataVersion"},"identity":{"$ref":"#/components/schemas/DataIdentity","description":"The Data Identity common to all versions of the same Data"},"payload":{"type":"object","description":"Data if exists in the commit, null otherwise"}},"required":["@id","@type","identity"]}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"OpenAPI definition","version":"v0"},"paths":{"/api/rest/projects/{projectId}":{"get":{"tags":["project-rest-controller"],"description":"Get project with the given id (projectId).","operationId":"getProjectById","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}}}},"put":{"tags":["project-rest-controller"],"description":"Update the project with the given id (projectId).","operationId":"updateProject","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"query","required":false,"schema":{"type":"string"}},{"name":"description","in":"query","required":false,"schema":{"type":"string"}},{"name":"branch","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Branch"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}}}},"delete":{"tags":["project-rest-controller"],"description":"Delete the project with the given id (projectId).","operationId":"deleteProject","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}}}}},"/api/rest/projects":{"get":{"tags":["project-rest-controller"],"description":"Get all projects.","operationId":"getProjects","parameters":[{"name":"page[size]","in":"query","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"page[after]","in":"query","required":false,"schema":{"type":"string"}},{"name":"page[before]","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}}}}}},"post":{"tags":["project-rest-controller"],"description":"Create a new project with the given name and description (optional).","operationId":"createProject","parameters":[{"name":"name","in":"query","required":true,"schema":{"type":"string"}},{"name":"description","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}}}}},"/api/rest/projects/{projectId}/commits":{"get":{"tags":["commit-rest-controller"],"description":"Get all the commits in the given project.","operationId":"getCommits","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Commit"}}}}}}},"post":{"tags":["commit-rest-controller"],"description":"Create a new commit with the given change (collection\nof DataVersion records) in the given branch of the\nproject. If the branch is not specified, the default branch\nof the project is used. Commit.change should include\nthe following for each Data object that needs to be\ncreated, updated, or deleted in the new commit. (1)\nCreating Data - Commit.change should include a\nDataVersion record with DataVersion.payload\npopulated with the Data being created.\nDataVersion.identity is not provided, thereby indicating\nthat a new DataIdentity needs to be created in the new\ncommit. (2) Updating Data - Commit.change should\ninclude a DataVersion record with DataVersion.payload\npopulated with the updated Data. DataVersion.identity\nshould be populated with the DataIdentity for which a\nnew DataVersion record will be created in the new\ncommit. (3) Deleting Data - Commit.change should\ninclude a DataVersion record with DataVersion.payload\nnot provided, thereby indicating deletion of DataIdentity\nin the new commit. DataVersion.identity should be\npopulated with the DataIdentity that will be deleted in\nthe new commit. When a DataIdentity is deleted in a\ncommit, all its versions (DataVersion) are also deleted,\nand any references from other DataIdentity are also\nremoved to maintain data integrity. In addition, for\nElement Data (KerML), deletion of an Element must\nalso result in deletion of incoming Relationships. When\nElement Data (KerML) is created or updated, derived\nproperties must be computed or verified if the API\nprovider claims Derived Property Conformance.\n","operationId":"createCommit","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"branchId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Commit"}}}}}}},"/api/rest/projects/{projectId}/commits/{commitId}":{"get":{"tags":["commit-rest-controller"],"description":"Get the commit with the given id (commitId) in the given project.","operationId":"getCommitById","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Commit"}}}},"404":{"description":"Not Found"}}}},"/api/rest/projects/{projectId}/commits/{commitId}/roots":{"get":{"tags":["object-rest-controller"],"description":"Get all the root elements in the given project at the given commit.","operationId":"getRootElements","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"example":[{"@id":"9f2e43d4-2f7e-49f1-816a-a4e0d3d1f370","attribute1":"hello"},{"@id":"bab1f7be-82e0-4d14-bc60-b12a60c46f2f","attribute1":"bye"}]}}}}}},"/api/rest/projects/{projectId}/commits/{commitId}/elements":{"get":{"tags":["object-rest-controller"],"description":"Get all the elements in a given project at the given commit.","operationId":"getElements","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"example":[{"@id":"9f2e43d4-2f7e-49f1-816a-a4e0d3d1f370","attribute1":"hello"},{"@id":"bab1f7be-82e0-4d14-bc60-b12a60c46f2f","attribute1":"bye"}]}}}}}},"/api/rest/projects/{projectId}/commits/{commitId}/elements/{relatedElementId}/relationships":{"get":{"tags":["object-rest-controller"],"description":"Get relationships that are incoming, outgoing, or both relative to the given related element.","operationId":"getRelationshipsByRelatedElement","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"relatedElementId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"direction","in":"query","required":false,"schema":{"type":"string","enum":["IN","OUT","BOTH"]}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"example":[{"@id":"9f2e43d4-2f7e-49f1-816a-a4e0d3d1f370","attribute1":"hello"},{"@id":"bab1f7be-82e0-4d14-bc60-b12a60c46f2f","attribute1":"bye"}]}}}}}},"/api/rest/projects/{projectId}/commits/{commitId}/elements/{elementId}":{"get":{"tags":["object-rest-controller"],"description":"Get element with the given id (elementId) in the given project at the given commit.","operationId":"getElementById","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"elementId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"404":{"description":"Not Found"},"200":{"description":"OK","content":{"application/json":{"example":{"@id":"9f2e43d4-2f7e-49f1-816a-a4e0d3d1f370","attribute1":"hello"}}}}}}},"/api/rest/projects/{projectId}/commits/{commitId}/changes":{"get":{"tags":["commit-rest-controller"],"description":"Get the change in the given commit of the given project.","operationId":"getCommitChange","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"changeTypes","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","enum":["CREATED","UPDATED","DELETED"]}}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DataVersion"}}}}},"404":{"description":"Not Found"}}}},"/api/rest/projects/{projectId}/commits/{commitId}/changes/{changeId}":{"get":{"tags":["commit-rest-controller"],"description":"Get the change with the given id (changeId) in the given commit of the given project. The changeId is the id of the DataVersion that changed in the commit.","operationId":"getCommitChangeById","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"commitId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"changeId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataVersion"}}}},"404":{"description":"Not Found"}}}}},"components":{"schemas":{"Branch":{"type":"object","description":"Branch is an indirect subclass of Record (via CommitReference) that represents an independent line of development in a Project. A Project can have 1 or more Branches. When a Project is created, a default Branch is also created. The default Branch of a Project can be changed, and a Project can have only 1 default Branch.","properties":{"@id":{"type":"string","format":"uuid","description":"The UUID assigned to the record"},"@type":{"type":"string","description":"Branch"},"created":{"type":"string","format":"date-time","description":"The timestamp at which the Branch was created, in ISO8601DateTime format"},"head":{"$ref":"#/components/schemas/Identified","description":"The Commit to which the Branch is currently pointing. It represents the latest state of the Project on the given Branch"},"name":{"type":"string","description":"The name of the Branch"},"owningProject":{"$ref":"#/components/schemas/Identified","description":"The Project that owns the given Branch"},"referencedCommit":{"$ref":"#/components/schemas/Identified","description":"The Commit referenced by the Branch"}},"required":["@id","@type","created","name","owningProject","referencedCommit"]},"Identified":{"type":"object","description":"Identified represents an Object through its ID and type.","properties":{"@id":{"type":"string"}}},"Project":{"type":"object","description":"Project is a subclass of Record that represents a container for other Records and an entry point for version management and data navigation.","properties":{"@id":{"type":"string","description":"The UUID assigned to the record"},"@type":{"type":"string","description":"Project"},"created":{"type":"string","format":"date-time","description":"The creation timestamp for the Project, in ISO8601DateTime format"},"defaultBranch":{"$ref":"#/components/schemas/Identified","description":"The default branch in the Project and a subset of branches"},"description":{"type":"string","description":"The statement that provides details about the record"},"name":{"type":"string","description":"The name of the Project"}},"required":["@id","@type","created","defaultBranch","name"]},"Commit":{"type":"object","description":"Commit is a subclass of Record that represents the changes made to a Project at a specific point in time in its lifecycle, such as the creation, update, or deletion of data in a Project. A Project has 0 or more Commits.","properties":{"@id":{"type":"string","format":"uuid","description":"The UUID assigned to the record"},"@type":{"type":"string","description":"Commit"},"created":{"type":"string","format":"date-time","description":"The timestamp at which the Commit was created, in ISO8601DateTime format"},"description":{"type":"string","description":"The statement that provides details about the record"},"owningProject":{"$ref":"#/components/schemas/Identified","description":"The Project that owns the Commit"},"previousCommits":{"type":"array","description":"The set of immediately preceding Commits","items":{"$ref":"#/components/schemas/Identified"}}},"required":["@id","@type","created","owningProject","previousCommits"]},"DataIdentity":{"type":"object","description":"DataIdentity is a subclass of Record that represents a unique, version-independent representation of Data through its lifecycle. A DataIdentity is associated with 1 or more DataVersion records that represent different versions of the same Data.","properties":{"@id":{"type":"string","format":"uuid","description":"The UUID assigned to the record"},"@type":{"type":"string","description":"DataIdentity"}},"required":["@id","@type"]},"DataVersion":{"type":"object","description":"DataVersion is a subclass of Record that represents Data at a specific version in its lifecycle. A DataVersion record is associated with only one DataIdentity record. DataVersion serves as a wrapper for Data (payload) in the context of a Commit in a Project.","properties":{"@id":{"type":"string","format":"uuid","description":"The UUID assigned to the record"},"@type":{"type":"string","description":"DataVersion"},"identity":{"$ref":"#/components/schemas/DataIdentity","description":"The Data Identity common to all versions of the same Data"},"payload":{"type":"object","description":"Data if exists in the commit, null otherwise"}},"required":["@id","@type","identity"]}}}} \ No newline at end of file diff --git a/doc/content/modules/developer-guide/examples/create_project.py b/doc/content/modules/developer-guide/examples/create_project.py new file mode 100644 index 00000000..12573e50 --- /dev/null +++ b/doc/content/modules/developer-guide/examples/create_project.py @@ -0,0 +1,50 @@ +# These examples are adapted from the SysML v2 API Cookbook, available at +# https://github.com/Systems-Modeling/SysML-v2-API-Cookbook, maintained by the +# Object Management Group (OMG). +# The original cookbook is designed for use with Jupyter Lab. +# These examples have been adapted to run as standalone Python scripts, making +# them suitable for use in various environments, including SysON. +# They showcase practical usage scenarios and may include additional functionality +# or modifications tailored to specific needs. + +import requests # <1> +from init_api import init_sysmlv2_api +from fetch_projects import fetch_projects +from datetime import datetime + +def create_project(host, project_name): # <2> + # Define project data as query parameters + project_params = { + "name": project_name + } + + # API endpoint to create a project + url = f"{host}/projects" # <3> + + # Send POST request with query parameters + response = requests.post(url, params=project_params) # <4> + + # Check if the project creation was successful + if response.status_code == 201: # <5> + response_json = response.json() + print("Project created successfully:") + print(f"Project ID: {response_json.get("@id", "Unknown ID")}") + print(f"Project Name: {response_json.get('name', 'Unknown Name')}") + else: + print(f"Error creating project: {response.status_code} - {response.text}") + +if __name__ == "__main__": + host = init_sysmlv2_api() + + # Fetch and display the list of projects currently available on the server + print("Fetching the list of projects currently available on the server:") + fetch_projects(host) + + # Create a new project with a unique name by appending a timestamp + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + project_name = f"New Project - {timestamp}" + create_project(host, project_name) + + # Fetch and display the updated list of projects after creating the new project + print("Fetching the updated list of projects after creating a new project:") + fetch_projects(host) \ No newline at end of file diff --git a/doc/content/modules/developer-guide/examples/fetch_commits.py b/doc/content/modules/developer-guide/examples/fetch_commits.py new file mode 100644 index 00000000..2e036468 --- /dev/null +++ b/doc/content/modules/developer-guide/examples/fetch_commits.py @@ -0,0 +1,43 @@ +# These examples are adapted from the SysML v2 API Cookbook, available at +# https://github.com/Systems-Modeling/SysML-v2-API-Cookbook, maintained by the +# Object Management Group (OMG). +# The original cookbook is designed for use with Jupyter Lab. +# These examples have been adapted to run as standalone Python scripts, making +# them suitable for use in various environments, including SysON. +# They showcase practical usage scenarios and may include additional functionality +# or modifications tailored to specific needs. + +import requests # <1> +from init_api import parse_arguments +from init_api import init_sysmlv2_api + +def fetch_commits(host, project_id): # <2> + commits_url = f"{host}/projects/{project_id}/commits" # <3> + response = requests.get(commits_url) # <4> + if response.status_code == 200: # <5> + commits = response.json() + for commit in commits: + print(f"Commit ID: {commit['@id']}") + return commits + else: + print(f"Error fetching commits: {response.status_code} - {response.text}") + return None + +# Retrieves the latest commit for a given project. +def get_last_commit_id(host, project_id): + commits = fetch_commits(host, project_id) + if commits: + last_commit = commits[-1] if commits else None + if last_commit: + last_commit_id = last_commit['@id'] + print(f"Last Commit ID: {last_commit_id}") + return last_commit_id + else: + print("No commits available.") + return None + +if __name__ == "__main__": + args = parse_arguments() + host = init_sysmlv2_api() + project_id = args.project_id + fetch_commits(host, project_id) diff --git a/doc/content/modules/developer-guide/examples/fetch_elements.py b/doc/content/modules/developer-guide/examples/fetch_elements.py new file mode 100644 index 00000000..b227f211 --- /dev/null +++ b/doc/content/modules/developer-guide/examples/fetch_elements.py @@ -0,0 +1,34 @@ +# These examples are adapted from the SysML v2 API Cookbook, available at +# https://github.com/Systems-Modeling/SysML-v2-API-Cookbook, maintained by the +# Object Management Group (OMG). +# The original cookbook is designed for use with Jupyter Lab. +# These examples have been adapted to run as standalone Python scripts, making +# them suitable for use in various environments, including SysON. +# They showcase practical usage scenarios and may include additional functionality +# or modifications tailored to specific needs. + +import requests # <1> +from init_api import parse_arguments +from init_api import init_sysmlv2_api +from fetch_commits import get_last_commit_id + +def fetch_elements(host, project_id): # <2> + commit_id = get_last_commit_id(host, project_id) + # API endpoint to fetch elements of the commit + element_get_url = f"{host}/projects/{project_id}/commits/{commit_id}/elements" # <3> + # Send GET request to retrieve elements + response = requests.get(element_get_url) # <4> + if response.status_code == 200: # <5> + elements = response.json() + for element in elements: + print(f"Element ID: {element['@id']}, Name: {element['name']}") + return elements + else: + print(f"Error fetching elements: {response.status_code} - {response.text}") + return None + +if __name__ == "__main__": + args = parse_arguments() + host = init_sysmlv2_api() + project_id = args.project_id + fetch_elements(host, project_id) \ No newline at end of file diff --git a/doc/content/modules/developer-guide/examples/fetch_projects.py b/doc/content/modules/developer-guide/examples/fetch_projects.py new file mode 100644 index 00000000..b4a2d192 --- /dev/null +++ b/doc/content/modules/developer-guide/examples/fetch_projects.py @@ -0,0 +1,26 @@ +# These examples are adapted from the SysML v2 API Cookbook, available at +# https://github.com/Systems-Modeling/SysML-v2-API-Cookbook, maintained by the +# Object Management Group (OMG). +# The original cookbook is designed for use with Jupyter Lab. +# These examples have been adapted to run as standalone Python scripts, making +# them suitable for use in various environments, including SysON. +# They showcase practical usage scenarios and may include additional functionality +# or modifications tailored to specific needs. + +import requests # <1> +from init_api import init_sysmlv2_api + +def fetch_projects(host): # <2> + projects_url = f"{host}/projects" # <3> + response = requests.get(projects_url) # <4> + if response.status_code == 200: # <5> + projects = response.json() + for project in projects: + print(f"Project Name: {project['name']}, ID: {project['@id']}") + else: + print(f"Error fetching projects: {response.status_code} - {response.text}") + +if __name__ == "__main__": + host = init_sysmlv2_api() + # Get the projects + fetch_projects(host) \ No newline at end of file diff --git a/doc/content/modules/developer-guide/examples/get_feature_members.py b/doc/content/modules/developer-guide/examples/get_feature_members.py new file mode 100644 index 00000000..3ca28b29 --- /dev/null +++ b/doc/content/modules/developer-guide/examples/get_feature_members.py @@ -0,0 +1,46 @@ +# These examples are adapted from the SysML v2 API Cookbook, available at +# https://github.com/Systems-Modeling/SysML-v2-API-Cookbook, maintained by the +# Object Management Group (OMG). +# The original cookbook is designed for use with Jupyter Lab. +# These examples have been adapted to run as standalone Python scripts, making +# them suitable for use in various environments, including SysON. +# They showcase practical usage scenarios and may include additional functionality +# or modifications tailored to specific needs. + +from init_api import parse_arguments +from init_api import init_sysmlv2_api +from fetch_commits import get_last_commit_id +from get_owned_elements import get_element + +def get_member_features(host, project_id, commit_id, element_id, member_type, indent): + # Fetch the element + element_data = get_element(host, project_id, commit_id, element_id, indent) + if element_data: + element_type = element_data ['@type'] + + if element_type == member_type: + print(element_data) + # Feature memberships + element_features = element_data['ownedFeature'] + if len(element_features) > 0: + for feature in element_features: + get_member_features(host, project_id, commit_id, feature['@id'], member_type, indent + " ") + +if __name__ == "__main__": + args = parse_arguments() + host = init_sysmlv2_api() + project_id = args.project_id + commit_id = get_last_commit_id(host, project_id) + element_id = args.element_id + + # Get Parts Tree + print("Parts Tree:") + get_member_features(host, project_id, commit_id, element_id, 'PartUsage', " ") + + # Get Behaviour Tree + print("Behaviours Tree:") + get_member_features(host, project_id, commit_id, element_id, 'ActionUsage', " ") + + # Get Requirements Tree + print("Requirements Tree:") + get_member_features(host, project_id, commit_id, element_id, 'RequirementUsage', " ") \ No newline at end of file diff --git a/doc/content/modules/developer-guide/examples/get_owned_elements.py b/doc/content/modules/developer-guide/examples/get_owned_elements.py new file mode 100644 index 00000000..288f463f --- /dev/null +++ b/doc/content/modules/developer-guide/examples/get_owned_elements.py @@ -0,0 +1,66 @@ +# These examples are adapted from the SysML v2 API Cookbook, available at +# https://github.com/Systems-Modeling/SysML-v2-API-Cookbook, maintained by the +# Object Management Group (OMG). +# The original cookbook is designed for use with Jupyter Lab. +# These examples have been adapted to run as standalone Python scripts, making +# them suitable for use in various environments, including SysON. +# They showcase practical usage scenarios and may include additional functionality +# or modifications tailored to specific needs. + +import requests +from init_api import parse_arguments +from init_api import init_sysmlv2_api +from fetch_commits import get_last_commit_id + +# Function to fetch an element and print its name and type +def get_element(host, project_id, commit_id, element_id, indent): + # Fetch the element in the given commit of the given project + element_url = f"{host}/projects/{project_id}/commits/{commit_id}/elements/{element_id}" # <3> + response = requests.get(element_url) # <1> + + if response.status_code == 200: + element_data = response.json() + element_name_to_print = element_data['name'] if element_data['name'] else 'N/A' + element_id = element_data ['@id'] + element_type = element_data ['@type'] + print(f"{indent} - {element_name_to_print} (id = {element_id} , type = {element_type})") # <2> + return element_data + else: + return None + +# Fetches immediate owned elements for a given element +def get_owned_elements_immediate(host, project_id, commit_id, element_id, indent): + element_data = get_element(host, project_id, commit_id, element_id, indent) + if element_data: + owned_elements = element_data['ownedElement'] + if len(owned_elements) > 0: + for owned_element in owned_elements: + get_element(host, project_id, commit_id, owned_element['@id'], indent + ' ') + else: + print(f"Unable to fetch element with id '{element_id}' in commit '{commit_id}' of project '{project_id}'") + +# Fetches owned elements recursively for a given element +def get_owned_elements(host, project_id, commit_id, element_id, indent): + element_data = get_element(host, project_id, commit_id, element_id, indent) + if element_data: # <3> + owned_elements = element_data['ownedElement'] + if len(owned_elements) > 0: + for owned_element in owned_elements: + get_owned_elements(host, project_id, commit_id, owned_element['@id'], indent+' ') + else: + print(f"Unable to fetch element with id '{element_id}' in commit '{commit_id}' of project '{project_id}'") # <4> + +if __name__ == "__main__": + args = parse_arguments() + host = init_sysmlv2_api() + project_id = args.project_id + commit_id = get_last_commit_id(host, project_id) + element_id = args.element_id + + #Get owned elements (immediate) for the given element in the given commit of the given project + print("Immediate Owned Elements:") + get_owned_elements_immediate(host, project_id, commit_id, element_id, '') + + # Get owned elements (recursive) for the given element in the given commit of the given project + print("\nRecursive Owned Elements:") + get_owned_elements(host, project_id, commit_id, element_id, '') \ No newline at end of file diff --git a/doc/content/modules/developer-guide/examples/init_api.py b/doc/content/modules/developer-guide/examples/init_api.py new file mode 100644 index 00000000..808cdec2 --- /dev/null +++ b/doc/content/modules/developer-guide/examples/init_api.py @@ -0,0 +1,29 @@ +# These examples are adapted from the SysML v2 API Cookbook, available at +# https://github.com/Systems-Modeling/SysML-v2-API-Cookbook, maintained by the +# Object Management Group (OMG). +# The original cookbook is designed for use with Jupyter Lab. +# These examples have been adapted to run as standalone Python scripts, making +# them suitable for use in various environments, including SysON. +# They showcase practical usage scenarios and may include additional functionality +# or modifications tailored to specific needs. +import argparse + +def init_sysmlv2_api(): + host = "http://localhost:8080/api/rest" # Replace with your actual API host URL # <1> + return host + +def parse_arguments(): + # Parse command-line arguments + parser = argparse.ArgumentParser() + parser.add_argument( + "project_id", + type=str, + help="The project ID.", + ) + parser.add_argument( + "element_id", + type=str, + nargs="?", # This makes element_id optional + help="The element ID (optional).", + ) + return parser.parse_args() \ No newline at end of file diff --git a/doc/content/modules/developer-guide/pages/api-cookbook.adoc b/doc/content/modules/developer-guide/pages/api-cookbook.adoc new file mode 100644 index 00000000..337cad27 --- /dev/null +++ b/doc/content/modules/developer-guide/pages/api-cookbook.adoc @@ -0,0 +1,63 @@ += APIs cookbook +The APIs cookbook offers a collection of practical examples and step-by-step instructions to help you interact with {product}’s REST APIs. +It includes ready-to-use code snippets and explanations, allowing you to integrate with other tools, automate tasks, and explore advanced features efficiently. +Each recipe aims for clarity, ease of use, and customizability for different use cases. + +== Setup instructions +To get started with the examples, Python is used to interact with {product}’s features and data. +Python offers a powerful way to automate tasks, handle API calls, and process data, making it an ideal choice for this purpose. +Follow the steps after to set up your environment and get everything ready to run the provided code snippets. + +=== Step 1: Install required Python libraries +Confirm you have the following Python libraries installed before running the code snippets: + +* `requests`: For making HTTP requests to the {sysmlv2} API. +* `pandas`: For organizing and manipulating data. + +To install these libraries, run: + +`pip install requests pandas` + +=== Step 2: Configure {sysmlv2} API access + +[source,python] +.init_api.py +---- +include::example$init_api.py[] +---- + +TIP: Replace `http://localhost:8080` with the actual URL of your API server if hosted elsewhere. + +*What this code does*: + +<1> Define the base host: Assigns the variable `host` with the base URL of the {sysmlv2} API server. + +[NOTE] +==== +In the examples, the {product} server uses a randomization function to generate unique IDs, so outputs can differ when you run the examples on your project. +==== + +Other code snippets use this function. +To run it, see the following section about recipes. + +=== Step 3: Test code snippets +Once you have configured the {sysmlv2} API access, you can start testing the xref:developer-guide:api-cookbook.adoc#recipes[recipes]. + +=== Troubleshooting + +. *Error Handling*: +** Check endpoint URLs, `project_id`, `commit_id`, and `authentication` tokens if the API returns errors: for example, 400 or 500 status codes. +** Confirm the API is running. +. *Empty Responses*: +** Verify that queried elements exist within the specified project and commit. +** Use the {product} web interface for visual inspection. +. *Recursive Function Depth*: +** For large models, manage recursion depth appropriately. +** Change the logic to handle large datasets by limiting recursion depth. + +[#recipes] +== Recipes + +include::developer-guide:project_commit_branch_tag_recipe.adoc[leveloffset=+2] + +include::developer-guide:element_owned_elements_recipe.adoc[leveloffset=+2] \ No newline at end of file diff --git a/doc/content/modules/developer-guide/pages/api-details.adoc b/doc/content/modules/developer-guide/pages/api-details.adoc new file mode 100644 index 00000000..6805d604 --- /dev/null +++ b/doc/content/modules/developer-guide/pages/api-details.adoc @@ -0,0 +1,70 @@ += API details + +The complete list of {sysmlv2} REST APIs is available on the https://www.omg.org/spec/SystemsModelingAPI/[OMG website]. + + +[IMPORTANT] +==== +*Endpoint Prefix*: All REST APIs in {product} start with `/api/rest/`. +==== + +[IMPORTANT] +==== +In {product}, each project uses a single commit and branch by default. +The commit ID and branch ID are same to the project ID. +The API for creating additional commits isn't functional in the current implementation. +==== + +== Supported REST APIs in {product} + +Below is the list of REST APIs supported in {product}, along with descriptions of key concepts used in REST APIs: + +++++ + +
+ +++++ + +[TIP] +==== +To test the API on your local server, navigate to: +http://localhost:8080/swagger-ui/index.html + +This URL opens the Swagger UI, an interactive documentation tool for APIs. +The interface provides an overview of API endpoints, supports testing with input parameters, and displays responses for better understanding. +==== \ No newline at end of file diff --git a/doc/content/modules/developer-guide/pages/api.adoc b/doc/content/modules/developer-guide/pages/api.adoc new file mode 100644 index 00000000..98560aab --- /dev/null +++ b/doc/content/modules/developer-guide/pages/api.adoc @@ -0,0 +1,28 @@ += APIs + +== {sysmlv2} Standard REST APIs + +[NOTE] +==== +The {sysmlv2} API isn't fully available yet, but we're actively developing it. +In the meantime, you can use the provided file exchange format to ensure interoperability with {product}. +==== + +{product} focuses on enabling seamless collaboration within the {mbse} - `MBSE` ecosystem. +With a strong emphasis on interoperability, {product} aims to support future standard APIs that will connect to a wide range of modeling tools and resources. + +Although we've set standard API implementation as a key development goal, {product} already provides a solid foundation for effective collaboration between tools, reinforcing its role as a powerful platform for MBSE practitioners. + + +{product}’s commitment to incorporating standard REST APIs and {sysmlv2} textual specifications highlights its dedication to delivering a flexible and interoperable modeling solution. + +Refer to the xref:developer-guide:api-details.adoc[API Details Section] for more information about the supported APIs. + +== APIs Cookbook + +For detailed examples, best practices, and guidance on using {product}’s REST APIs, consult the xref:developer-guide:api-cookbook.adoc[APIs Cookbook]. +The cookbook includes: + +* Step-by-step guides for common tasks. +* Code snippets for API interactions. +* Explanations of core API concepts. \ No newline at end of file diff --git a/doc/content/modules/developer-guide/pages/element_owned_elements_recipe.adoc b/doc/content/modules/developer-guide/pages/element_owned_elements_recipe.adoc new file mode 100644 index 00000000..52c8fafb --- /dev/null +++ b/doc/content/modules/developer-guide/pages/element_owned_elements_recipe.adoc @@ -0,0 +1,80 @@ += Element owned elements recipe + +Learn how to retrieve owned elements programmatically. +Each recipe includes a detailed explanation, step-by-step instructions, and sample code. + +Recipes covered: + +* xref:developer-guide:element_owned_elements_recipe.adoc#get_owned_elements[Get Owned Elements]: Retrieve owned elements. + +[#get_owned_elements] +== Get owned elements +This example demonstrates how to recursively navigate through the hierarchical structure of elements by using the {product} API. +The script starts from a specific element identified by its ID and retrieves its details along with any "owned elements." +The function calls itself recursively to explore all child elements in the hierarchy, printing their names, IDs, and types in an indented format to show the hierarchy visually. + +Example script to get owned elements: + +[source,python] +.get_owned_element.py +---- +include::example$get_owned_elements.py[] +---- + +*What this code does*: + +<1> *Fetches an Element*: Sends a `GET` request to the {product} API to retrieve details about an element specified by its ID. + +<2> *Prints Element Details*: Displays the element's name, ID, and type in a formatted, indented way. + +<3> *Processes Owned Elements*: Checks for owned elements and calls itself recursively for each one. + +<4> *Recursive Traversal*: Continues recursing through hierarchical levels until it explores and prints all owned elements. + +This approach helps visualize the structure of complex models or systems, where elements are organized hierarchically. +It highlights how to navigate relationships between elements efficiently by using the {product} API. + +Run the script: +[source,bash] +---- +$ python get_owned_elements.py +---- + +Output example: +[source,bash] +---- +Commit ID: 63a03bd8-a81a-4818-801a-01790ce8a086 +Last Commit ID: 63a03bd8-a81a-4818-801a-01790ce8a086 +Immediate Owned Elements: + - Batmobile (Definition) + - problemStatement (ReferenceUsage) + - systemIdea (ReferenceUsage) + - seat (PartUsage) + - body (PartUsage) + - wheels (PartUsage) + - frontLeftWheel (PartUsage) + - frontRightWheel (PartUsage) + - rearLeftWheel (PartUsage) + - rearRightWheel (PartUsage) + - battery (PartUsage) + - batmobileEngine (PartUsage) + +Recursive Owned Elements: + - Batmobile (Definition) + - problemStatement (ReferenceUsage) + - N/A (LiteralString) + - systemIdea (ReferenceUsage) + - N/A (LiteralString) + - seat (PartUsage) + - body (PartUsage) + - wheels (PartUsage) + - frontLeftWheel (PartUsage) + - frontRightWheel (PartUsage) + - rearLeftWheel (PartUsage) + - rearRightWheel (PartUsage) + - battery (PartUsage) + - powerPort (PortUsage) + - capacity (AttributeUsage) + - batmobileEngine (PartUsage) + - enginePort (PortUsage) +---- \ No newline at end of file diff --git a/doc/content/modules/developer-guide/pages/index.adoc b/doc/content/modules/developer-guide/pages/index.adoc index 591f2c1a..c889554a 100644 --- a/doc/content/modules/developer-guide/pages/index.adoc +++ b/doc/content/modules/developer-guide/pages/index.adoc @@ -65,4 +65,10 @@ Here are the steps to follow: .. add `#!/usr/bin/env node` as first line .. add `src_default();` as last line .. rename it to `syside-cli.js` -. Copy the renamed file into `your_local_syson_repo/backend/application/syson-import/src/main/resources` (overwrite the existing file). \ No newline at end of file +. Copy the renamed file into `your_local_syson_repo/backend/application/syson-import/src/main/resources` (overwrite the existing file). + +== Generate a new OpenAPI documentation of REST API + +. Build and run the syson server locally (using docker compose as example) on `http://localhost:8080`. +. Download the documentation at the url: `http://localhost:8080/v3/api-docs/rest-apis`. +. The swagger-ui user interface is `http://localhost:8080/swagger-ui/index.html`. \ No newline at end of file diff --git a/doc/content/modules/developer-guide/pages/project_commit_branch_tag_recipe.adoc b/doc/content/modules/developer-guide/pages/project_commit_branch_tag_recipe.adoc new file mode 100644 index 00000000..b63fe9d4 --- /dev/null +++ b/doc/content/modules/developer-guide/pages/project_commit_branch_tag_recipe.adoc @@ -0,0 +1,158 @@ += Project and commit recipe + +Learn how to manage projects and retrieve related commits programmatically. +Each recipe includes a detailed explanation, step-by-step instructions, and code snippets. + +Recipes covered: + +* xref:project_commit_branch_tag_recipe.adoc#_get_projects[Get Projects]: Fetch and display all projects currently available on the server. +* xref:project_commit_branch_tag_recipe.adoc#_create_a_new_project[Create a New Project]: Create a new project with a unique name. +* xref:project_commit_branch_tag_recipe.adoc#_get_commits[Get Commits]: Retrieve a list of commits for a specific project. + +== Get projects +This code example demonstrates how to use Python's requests library to interact with the {product} API and retrieve a list of projects. +It sends a `GET` request to the API endpoint for projects, processes the response, and prints the name and ID of each project. + +Example script to fetch projects: + +[source,python] +.fetch_projects.py +---- +include::example$fetch_projects.py[] +---- + +*What this code does*: + +<1> *Import required libraries*: + +* `requests`: Used for sending HTTP requests. +<2> *Define the `fetch_projects` function* with one parameter: + +* `host`: The base API address. + +<3> Constructs the *API endpoint* address for fetching projects. + +<4> *Sends a `GET` request* to the API, passing the {product} address as a query parameter. + +<5> *Handles the API response*: + +* If the projects are successfully fetched - HTTP status `200`, the function prints all the projects ID and name. +* If an error occurs, the status code and error message are displayed. + +Run the script: + +[source,bash] +---- +$ python fetch_projects.py +---- + +Output: + +[source,bash] +---- +Project Name: Batmobile, ID: 63a03bd8-a81a-4818-801a-01790ce8a086 +---- + +== Create a new project +This recipe demonstrates how to create a new project in {product} using Python. +It sends a `POST` request to the `/projects` endpoint to create a new project with a unique name and description. + +Example script to create a new project: + +[source,python] +.create_project.py +---- +include::example$create_project.py[] +---- + +*What this code does*: + +<1> *Import required libraries*: + +* `requests`: Used for sending HTTP requests. +* `datetime`: Used to generate a timestamp for unique project naming. +<2> *Define the `create_project` function* with two parameters: + +* `host`: The base API URL. +* `project_name`: The name of the project to be created. + +<3> Constructs the *API endpoint* URL of project creation. + +<4> *Sends a `POST` request* to the API, passing the project name as a query parameter. + +<5> *Handles the API response*: + +* If the project is successfully created - HTTP status `201`, the function extracts and prints the project ID and name. +* If an error occurs, the status code and error message are displayed. + +Run the script: + +[source,bash] +---- +$ python create_project.py +---- + +Output: + +[source,bash] +---- +Fetching the list of projects currently available on the server: +Project Name: Batmobile, ID: 63a03bd8-a81a-4818-801a-01790ce8a086 +Project created successfully: +Project ID: d967b937-304f-43a9-8af6-b1f3a9d6adbe +Project Name: New Project - 2024-12-31 10:52:56 +Fetching the updated list of projects after creating a new project: +Project Name: Batmobile, ID: 63a03bd8-a81a-4818-801a-01790ce8a086 +Project Name: New Project - 2024-12-31 10:52:56, ID: d967b937-304f-43a9-8af6-b1f3a9d6adbe +---- + +== Get commits + +[IMPORTANT] +==== +{product} always returns a *single branch* with a *single commit*. +==== + +This example extends the functionality by fetching the commit associated with a specific project ID. +It constructs an address based on the host and project ID, sends a `GET` request, and prints the ID of the retrieved commit. + +Example script to fetch commits: + +[source,python] +.fetch_commits.py +---- +include::example$fetch_commits.py[] +---- + +*What this code does*: + +<1> *Import required libraries*: + +* `requests`: Used for sending HTTP requests. +<2> *Define the `fetch_commits` Function* with two parameters: + +* `host`: The base API URL. +* `project_id`: The project ID. + +<3> Constructs the *API endpoint* URL for fetching commits. + +<4> *Sends a `GET` request* to the API, passing the {product} address and the project ID as a query parameter. + +<5> *Handles the API response*: + +* If the projects are successfully fetched - HTTP status `200`, the function prints all the projects ID and name. +* If an error occurs, the status code and error message are displayed. + +Run the script: + +[source,bash] +---- +$ python fetch_commits.py your-project-id +---- + +Output: + +[source,bash] +---- +Commit ID: 63a03bd8-a81a-4818-801a-01790ce8a086 +---- \ No newline at end of file diff --git a/doc/content/modules/developer-guide/partials/nav.adoc b/doc/content/modules/developer-guide/partials/nav.adoc index 5874c8b5..7ae55d44 100644 --- a/doc/content/modules/developer-guide/partials/nav.adoc +++ b/doc/content/modules/developer-guide/partials/nav.adoc @@ -2,4 +2,7 @@ // // A navigation file contains one or more AsciiDoc lists. // Each navigation file must be declared in the component descriptor if you want it to be displayed in the component’s navigation menu. -* xref:developer-guide:index.adoc[] \ No newline at end of file +* xref:developer-guide:index.adoc[] +** xref:developer-guide:api.adoc[] +*** xref:developer-guide:api-details.adoc[] +*** xref:developer-guide:api-cookbook.adoc[] \ No newline at end of file diff --git a/doc/content/modules/user-manual/assets/images/batmobile-project.png b/doc/content/modules/user-manual/assets/images/batmobile-project.png new file mode 100644 index 0000000000000000000000000000000000000000..abe1d5e4373eb77a209cefd5ff83a9dbfb611e23 GIT binary patch literal 60490 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 eA3Ws0-{KSRB -
- -++++ diff --git a/doc/content/modules/user-manual/pages/integration/developer-guide.adoc b/doc/content/modules/user-manual/pages/integration/developer-guide.adoc deleted file mode 100644 index 39b751a2..00000000 --- a/doc/content/modules/user-manual/pages/integration/developer-guide.adoc +++ /dev/null @@ -1,74 +0,0 @@ -= Developer guide - -This developer guide provides step-by-step instructions on how to set up your developer environment, contribute to the codebase, and enhance the capability of {product}. - -== Retrieving the source code - -{product} is built upon the https://eclipse.dev/sirius/sirius-web.html[Eclipse Sirius Web] project. -{product} is licensed under the xref:product-legal:index.adoc[(EPL v2)] Open Source license -The source code is openly available on GitHub:{source-code-url} - -To get the source code, clone the repository by using either SSH: - -[source, bash] ----- -git clone git@github.com:eclipse-syson/syson.git ----- - -or HTTPS: - -[source, bash] ----- -git clone https://github.com/eclipse-syson/syson.git ----- - -== Setting up your development environment - -. Download your coding environment https://spring.io/tools[Spring Tools 4.22.0] -. Ensure that m2e version from your environment is 2.6.0 -+ -image::environment-m2e-version.png[m2e version] -+ -. Retrieve the source code or fork it if you want to make contribution -. Import _backend_ plugins from syson repository in your workspace -. Update _settings.xml_ file from your _.m2_ folder to give access to other repositories during the build - To see dependance with access to add in _settings.xml_, have a look on _backend\application\syson-application\pom.xml_ - You need to create https://github.com/settings/tokens[access tokens] on Github to complete _settings.xml_. -+ -image::environment-settings.png[settings file] -+ -. Right click _syson-services_ module and then _Properties>JavaBuildPath>Source_ - Add _main/generated_ folder and remove _excluded_ content and former _antlr/grammar_ - You should retrieve the following _Java build path_ -+ -image::environment-java-build-path.png[Java build path] -+ -. Update Maven project by launching "ALT+F5" shortcut on all modules of your workspace - -== Generate a new version of SysIDE CLI - -In the _syson-import_ module, you'll found a `syside-cli-.js` file in `src/main/resources`. -This file is used by the import process, to parse the {sysmlv2} textual files. -If you want to update the version on SysIDE used to parse textual files, you need to generate a new version of this `syside-cli-.js` file. - -Here are the steps to follow: - -. Clone the https://github.com/sensmetry/sysml-2ls[SysIDE repository] from github. -. Install https://pnpm.io/installation[pnpm]. -. Go to the root of your local SysIDE repository. -. Execute `pnpm install` command. -. Execute `pnpm run build`command. -. Execute `pnpm run --dir packages/syside-cli/ esbuild` command. -. It produces a `your_local_syside_repo/packages/syside-cli/out/index.js` file -. Get a copy of the `your-local-syside-repo/packages/syside-cli/out/index.js` file. -. Edit this file: -.. add `#!/usr/bin/env node` as first line -.. add `src_default();` as last line -.. rename it to `syside-cli.js` -. Copy the renamed file into `your_local_syson_repo/backend/application/syson-import/src/main/resources` (overwrite the existing file). - -== Generate a new OpenAPI documentation of REST API - -. Build and run the syson server locally (using docker compose as example) on `localhost:8080`. -. Download the documentation at the url http://localhost:8080/v3/api-docs/rest-apis -. The swagger-ui user interface is `http://localhost:8080/swagger-ui/index.html#/` \ No newline at end of file diff --git a/doc/content/modules/user-manual/pages/key-features.adoc b/doc/content/modules/user-manual/pages/key-features.adoc index c9184f54..3c3134da 100644 --- a/doc/content/modules/user-manual/pages/key-features.adoc +++ b/doc/content/modules/user-manual/pages/key-features.adoc @@ -80,7 +80,7 @@ a|❌ |xref:integration/interoperability.adoc[Download / Upload {sysmlv2} textual format] a|✅ (Partial) -|xref:integration/api.adoc[SysML v2 Standard API] +|xref:developer-guide:api.adoc[SysML v2 Standard API] a|✅ (Partial) |=== diff --git a/doc/content/modules/user-manual/pages/release-notes/2024.11.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2024.11.0.adoc index 0b4b57f7..cc15ebc1 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2024.11.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2024.11.0.adoc @@ -6,7 +6,7 @@ All standard libraries have also been updated. - *Introduce SysMLv2 REST APIs*: introduce partial support of the standard REST APIs from the OMG https://www.omg.org/spec/SystemsModelingAPI/[SystemsModelingAPI]. In this first implementation we have implemented Project-related & Object-related REST APIs. -Please read the xref:/integration/api.adoc[APIs section] in this documentation for more details. +Please read the xref:developer-guide:api.adoc[APIs section] in this documentation for more details. == Breaking changes @@ -101,9 +101,9 @@ Added source sysml file and unaltered ast.json result. == New features -- Add a new Custom node representing à _Note_ among possible node style descriptions available. +- Add a new Custom node representing à _Note_ among possible node style descriptions available. The custom node is resizable and the text content is wrapped (back to the line) if it is too long compare to the node size. -- Represent `Documentation` as _Note_ graphical node. +- Represent `Documentation` as _Note_ graphical node. The `Documentation` graphical node can appear when user drag and drop `Documentation` element from explorer on the diagram. The `Documentation` graphical node is linked to its documented element by an edge and the node can only appear if the documented element is represented on the diagram. - Display prefix keywords in labels of `Documentation` graphical nodes. @@ -111,7 +111,7 @@ The `Documentation` graphical node is linked to its documented element by an edg image::release-notes-documentation-note.png[Documentation note node] - Allow creation of `Comment` from the Explorer view. -- Add `Comment` representation in graphical views. +- Add `Comment` representation in graphical views. The `Comment` graphical node can appear when user drag and drop `Comment` element from explorer on the diagram but also when user handle creation tool from the palette. The `Comment` graphical node is linked to its annotated element by an edge and the node can only appear if the annotated element is represented on the diagram. - Display prefix keywords in labels of `Comment` graphical nodes. diff --git a/doc/content/modules/user-manual/partials/nav-integration.adoc b/doc/content/modules/user-manual/partials/nav-integration.adoc index 6d64672e..f4393a5a 100644 --- a/doc/content/modules/user-manual/partials/nav-integration.adoc +++ b/doc/content/modules/user-manual/partials/nav-integration.adoc @@ -1,5 +1,5 @@ -* xref:user-manual:integration/developer-guide.adoc[] +* xref:developer-guide:index.adoc[] * xref:developer-guide:extend.adoc[] * xref:user-manual:integration/interoperability.adoc[] * xref:user-manual:integration/capella.adoc[] -* xref:user-manual:integration/api.adoc[] \ No newline at end of file +* xref:developer-guide:api.adoc[] \ No newline at end of file