Add new rtd subprojects commands to allow for subproject CRUD operations.
Signed-off-by: DW Talton <dtalton@contractor.linuxfoundation.org>
Change-Id: I9004a1e9a38dd939d3752dfb9b2efb3ba366d113
result = self.post('projects/{}/versions/{}/builds/'
.format(project, version))[1]
return result
+
+ def subproject_list(self, project):
+ """Return a list of subprojects.
+
+ This returns the list of subprojects by their slug name ['slug'],
+ not their pretty name ['name'].
+
+ :param kwargs:
+ :return: [subprojects]
+ """
+ result = self.get('projects/{}/subprojects/'.format(project))[1] # NOQA
+ more_results = None
+ data = result['results']
+ subproject_list = []
+
+ if result['next']:
+ more_results = result['next'].rsplit('/', 1)[-1]
+
+ if more_results:
+ while more_results is not None:
+ get_more_results = self.get('projects/{}/subprojects/'
+ .format(project) + more_results)[1]
+ data.append(get_more_results['results'])
+ more_results = get_more_results['next']
+
+ if more_results is not None:
+ more_results = more_results.rsplit('/', 1)[-1]
+
+ for subproject in data:
+ subproject_list.append(subproject['child']['slug'])
+
+ return subproject_list
+
+ def subproject_details(self, project, subproject):
+ """Retrieve the details of a specific subproject.
+
+ :param project:
+ :param subproject:
+ :return:
+ """
+ result = self.get('projects/{}/subprojects/{}/'
+ .format(project, subproject))[1]
+ return result
+
+ def subproject_create(self, project, subproject, alias=None):
+ """Create a subproject.
+
+ Subprojects are actually just top-level projects that
+ get subordinated to another project. Create the subproject
+ using project_create, then make it a subproject with
+ this function.
+
+ :param project: The top-level project's slug
+ :param subproject: The other project's slug that is to be subordinated
+ :param alias: An alias (not required). (user-defined slug)
+ :return:
+ """
+
+ data = {
+ 'child': subproject,
+ 'alias': alias
+ }
+ json_data = json.dumps(data)
+ result = self.post('projects/{}/subprojects/'
+ .format(project), data=json_data)
+ return result
+
+ def subproject_delete(self, project, subproject):
+ """Deletes the project/sub relationship.
+
+ :param project:
+ :param subproject:
+ :return:
+ """
+ result = self.delete('projects/{}/subprojects/{}/'
+ .format(project, subproject))
+
+ if hasattr(result, 'status_code'):
+ if result.status_code == 204:
+ return True
+ else:
+ return False, result.status_code
+ else:
+ return False
log.info(pformat(data))
+@click.command(name='subproject-list')
+@click.argument('project-slug')
+@click.pass_context
+def subproject_list(ctx, project_slug):
+ """Get a list of Read the Docs subprojects for a project.
+
+ Returns a list of RTD subprojects for a given
+ project.
+ """
+ r = readthedocs.ReadTheDocs()
+ for subproject in r.subproject_list(project_slug):
+ log.info(subproject)
+
+
+@click.command(name='subproject-details')
+@click.argument('project-slug')
+@click.argument('subproject-slug')
+@click.pass_context
+def subproject_details(ctx, project_slug, subproject_slug):
+ """Retrieve subproject's details."""
+ r = readthedocs.ReadTheDocs()
+ data = r.subproject_details(project_slug, subproject_slug)
+ log.info(pformat(data))
+
+
+@click.command(name='subproject-create')
+@click.argument('project-slug')
+@click.argument('subproject-slug')
+@click.pass_context
+def subproject_create(ctx, project_slug, subproject_slug):
+ """Creates a project-subproject relationship."""
+ r = readthedocs.ReadTheDocs()
+ data = r.subproject_create(project_slug, subproject_slug)
+ log.info(pformat(data))
+
+
+@click.command(name='subproject-delete')
+@click.argument('project-slug')
+@click.argument('subproject-slug')
+@click.pass_context
+def subproject_delete(ctx, project_slug, subproject_slug):
+ """Deletes a project-subproject relationship."""
+ r = readthedocs.ReadTheDocs()
+ data = r.subproject_delete(project_slug, subproject_slug)
+ if data:
+ log.info("Successfully removed the {} {} relationship"
+ .format(project_slug, subproject_slug))
+ else:
+ log.error("Request failed. Is there a subproject relationship?")
+
+
rtd.add_command(project_list)
rtd.add_command(project_details)
rtd.add_command(project_version_list)
rtd.add_command(project_build_list)
rtd.add_command(project_build_details)
rtd.add_command(project_build_trigger)
+rtd.add_command(subproject_list)
+rtd.add_command(subproject_details)
+rtd.add_command(subproject_create)
+rtd.add_command(subproject_delete)
--- /dev/null
+---
+features:
+ - |
+ Add support for RTD subprojects, including list, details, create, delete.
--- /dev/null
+{
+ "child":"testproject2"
+}
--- /dev/null
+{
+ "child": {
+ "slug": "testproject2"
+ }
+}
--- /dev/null
+{
+ "next": null,
+ "results": [
+ {
+ "child": {
+ "slug": "testproject2"
+ }
+ }
+ ]
+}
url='https://readthedocs.org/api/v3/projects/testproject1/versions/latest/builds/', # noqa
json=data, status=201)
assert rtd.project_build_trigger('testproject1', 'latest')
+
+
+@pytest.mark.datafiles(os.path.join(FIXTURE_DIR, 'rtd'),)
+@responses.activate
+def test_subproject_list(datafiles):
+ os.chdir(str(datafiles))
+ json_file = open('subproject_list.json', 'r')
+ json_data = json.loads(json_file.read())
+ responses.add(responses.GET,
+ url='https://readthedocs.org/api/v3/projects/TestProject1/subprojects/', # noqa
+ json=json_data, status=200, match_querystring=True)
+ assert 'testproject2' in rtd.subproject_list('TestProject1')
+
+
+@pytest.mark.datafiles(os.path.join(FIXTURE_DIR, 'rtd'),)
+@responses.activate
+def test_subproject_details(datafiles):
+ os.chdir(str(datafiles))
+ json_file = open('subproject_details.json', 'r')
+ json_data = json.loads(json_file.read())
+ responses.add(responses.GET,
+ url='https://readthedocs.org/api/v3/projects/TestProject1/subprojects/testproject2/', # NOQA
+ json=json_data, status=200)
+ assert 'child' in rtd.subproject_details('TestProject1', 'testproject2')
+
+
+@responses.activate
+def test_subproject_create():
+ responses.add(responses.POST,
+ url='https://readthedocs.org/api/v3/projects/TestProject1/subprojects/', # NOQA
+ status=201)
+ assert rtd.subproject_create('TestProject1', 'testproject2')
+
+
+def test_subproject_delete():
+ assert "untested because responses doesn't have DELETE support"