conduit API extensions for subprojects and milestones.
Closed, ResolvedPublic

Description

We're working on a FDA compliant workflow that requires us to document in an external system all of the tasks that constitute a single release. Ideally we would like to use a milestone to represent a release, assign tasks to it, and be able to use conduit to extract all of the information we need. A command line tool would take a single argument, the id of the milestone, and then be able to extract all of the information we need. We use custom fields to encode the additional metadata we need. Ideally, this is what we need:

given the id of a milestone:

  1. obtain the parent projects of that milestone
  2. obtain the set of tasks assigned to that milestone via a workboard

Another possible approach, which would is likely way more work for you, is to use workboards to represent releases - this would probably be a nicer UI in the long run, but we can live with the clunkier UI where each release is a sub-project too.

Currently, 1 does not appear possible for any sub-project and 2 does not appear possible for a milestone, but it is for a sub-project.

cos created this task.Jan 6 2017, 5:01 AM

For (2), the specific problem is that there's no way to look up the milestone PHID, right? If you have the PHID I believe maniphest.search lets you find tasks tagged with a particular milestone:

epriestley@orbital ~/dev/phabricator $ cat task-search.json 
{
  "constraints": {
    "projects": [
      "PHID-PROJ-5wgoiec3kt5evyiqvan3"
    ]
  }
}
epriestley@orbital ~/dev/phabricator $ cat task-search.json | arc call-conduit --conduit-uri=http://local.phacility.com/ maniphest.search | json_pp
{
   "errorMessage" : null,
   "error" : null,
   "response" : {
      "cursor" : {
         "order" : null,
         "limit" : 100,
         "before" : null,
         "after" : null
      },
      "maps" : [],
      "data" : [
         {
            "phid" : "PHID-TASK-ublnyrz4puidgwz44fvv",
            "fields" : {
               "ownerPHID" : null,
               "authorPHID" : "PHID-USER-cvfydnwadpdj7vdon36z",
               "status" : {
                  "value" : "open",
                  "color" : null,
                  "name" : "Open"
               },
               "priority" : {
                  "value" : 90,
                  "color" : "violet",
                  "name" : "Needs Triage",
                  "subpriority" : 0
               },
               "policy" : {
                  "edit" : "users",
                  "view" : "users"
               },
               "dateModified" : 1483727232,
               "spacePHID" : "PHID-SPCE-plg2qshv7yqgwy3beo7i",
               "name" : "Example Task",
<... snip ...>

However, project.search does not return milestones, so you can't go from an ID to a PHID. This is just an artifact of not showing milestones on the /project/ list view in the web UI, not an explicit design choice.


Broadly, after implementing projects/milestones we left some things intentionally undefined to collect feedback about use cases (some discussion in T10349) before making further product decisions. This included updates to the API, partly because querying via the API and querying via the web UI now use the same code and we were kicking around some ideas for refreshing the web UI (some very brief discussion in T4011 and elsewhere, maybe linked/merged from there). However, it's been quite a while and we haven't really seen any surprising use cases in feedback, so there's no reason this can't move forward now.

Here's how I expect to move forward:

  • I'm going to collect other subproject/milestone API use cases here and review them, since I think we've received a couple of requests, they just didn't have especially strong use case information.
  • I'll update the Project search engine in the upstream to support retrieving milestones, retrieving project type information, retrieving parent and/or ancestor information, querying by parent, and maybe querying by ancestor and querying by type, or some similar set of features based on this use case and other use cases I dig up.
  • We'll maintain and support this code going forward as part of the upstream.

I don't plan to get to this today since I'd want to give it more than 12 hours to stabilize in master before promoting it to stable, but I expect we can complete it by next week's release (circa Jan 13), and it will be available in master sometime before then.

The only possible roughness I anticipate is that the web UI and API should have different defaults in this case (the default list view in the web UI should not include milestones, but the default query result in the API should) which I think is something we haven't hit in other applications. However, I don't expect this to be difficult to work through.

Let us know if you have any questions.


Another possible approach, which would is likely way more work for you, is to use workboards to represent releases.

This is something I expect we'll support eventually, but, yeah, currently more complex than providing parent/subproject support. There's some discussion in T5214, particularly T5214#165992.

Briefly, I anticipate workboard column backend changes (mostly T5793 and T5474) which might require us to substantially break any API we wrote today by generalizing workboards and columns. I'm not sure if we'll pursue these changes (or if they'll require generalizations if we do) but I'm hesitant to provide an API which we know may break in the next iteration. Additionally, almost all use cases we've received about querying workboard columns appear to be for chart generation, and we'd prefer to pursue that goal directly (T1562) if that's the root problem we're solving.

That said, some aspects of API access to columns are not contentious, and I may be able to give you everything you want for your use case without creating any peril here. In particular, I think we can provide these capabilities reasonably:

  • When querying for tasks, retrieve information about which columns the task is in.
  • Given a column PHID, retrieve additional information about the column itself.

This is the potentially problematic capability, mostly because it's possible that T5793 eventually introduces multiple workboards per object (so users can have several personal workboards) and this query becomes meaningless:

  • Query for all columns attached to a project.

That said, we're in substantially better shape on all of this now than the last time this came up (for example, T6027 improved a lot of the transaction code) and I think we have a clearer picture of where we expect the product to go. I'll review exactly where we are and see if we can reasonably move forward yet.

I briefly dug these up:

  • T10436 wanted to use a project's position in a hierarchy as type information about the project. This isn't unreasonable but limits you to one type label and there are many workarounds (like using the project name, icon, color, or custom fields), and it does not particularly inform API design.
  • T10540 did not describe a use case.

I'd remembered more than this so maybe some more will turn up, but I think more of the discussion in the past may have focused on workboard column APIs (T5214) where most (all?) use cases appear to be charting.

cos added a comment.Jan 8 2017, 2:27 AM

I am currently using project.query rather than project.search. I presume the change you suggest for .search would work for query too since my recollection is that project.query also doesn't return milestones. I tried to use .search originally, but noticed that the attachments available were limited and didn't correspond to the documentation (examples mentions projects, subscribers, but then later on only watchers and members are supported) so I was scared off by the 'may change' warning:-) I can switch to using .search if that's the better thing to do - I presume you'd add attachments for the new types of information to be returned?

For the workboard related approach, you mention that 'Given a column PHID, retrieve additional information about the column itself.' is possible. How would I get the column PHID - the only way I found of doing so today is via maniphest.gettasktransactions??

Thanks!

The *.search endpoints are preferred now, but the guidance we provide on the Conduit page could use some updating. I'll rework this a little alongside other changes here.

I think the attachments should be consistent with the documentation, do you remember where you saw the inconsistency? Notably, attachments are per-object, so Tasks (maniphest.search) and Projects (project.search) have different types of attachments. Maybe the docs are misleading or could be more clear about this?

Tasks should support subscribers and projects attachments, via maniphest.search.

Projects should support members and watchers attachments, via project.search. They don't support subscribers because projects don't have subscribers (they did, long ago, but we removed them to simplify things since members, watchers, and subscribers had a lot of overlap and it was sometimes confusing). They don't support projects because you can't tag a project with other projects.

How would I get the column PHID

I'm not 100% decided on a plan yet, but I think we'll add a "columns" attachment to maniphest.search. You're correct that there's no (reasonable) way to get this information right now.


Here's my tentative plan:

  • Allow project.search to return all projects, including milestones.
  • Provide something like a type attribute (like "project", "subproject" or "milestone") on project.search.
  • Provide parent information on project.search, probably:
    • Provide a parent property on project.search.
    • Provide an ancestors attachment on project.search to get the whole chain of parents.
  • Provide parentPHIDs and ancestorPHIDs constraints on project.search, probably?
  • Provide a types or isMilestone constraint on project.search -- although I'm not quite sure yet if "project" and "subproject" should really be considered different types or not. I sort of lean toward perhaps treating milestone as a flag rather than project/subproject as types.
  • Provide a "columns" attachment on maniphest.search.
  • Update the guidance on the "v3" (*.search and *.edit) API endpoints to better communicate that they're now preferred.

And also expect to add:

  • A new API endpoint (like project.workboard.column.search, although that feels clunky) for retrieving column information.
  • A boardPHIDs constraint for that endpoint.

...but I want to review and plan our way through T5214 in a little more detail first. Let me dig into that now.

After reviewing the state of the world in related tasks:

We held some flexibility in place to possibly accommodate use cases associated with T5793, specifically "maybe anything, not just projects, will be able to have workboards in the future" and "objects may be able to have more than one workboard".

This flexibility impacts this task, since one model ("only projects have workboards") suggests that methods should be like project.board.search and accept projectPHIDs to identify boards. The other model ("anything can have any number of workboards") would suggest a method like workboard.search with new top-level board objects that have their own boardPHIDs because associated object PHIDs would no longer identify them.

T5793 discusses personal workboards, specifically, but if we generalized in that way we would likely want to at least make it possible for any object to have a workboard and probably for any object to have an arbitrary number of boards.

Other use cases have not arisen, and workboards have become more coupled with projects through existing features like milestones and subprojects and planned features like column triggers. To move forward here, I plan to discard the flexibility we've held in place and pursue the current, simpler model:

  • lock boards to projects; and
  • lock boards to one-per-project.

This approach does not preclude the eventual implementation of personal workboards (or other types of objects having workboards), but they would almost certainly act through an intermediary project which was flagged in some way to give it some special "personal project" behaviors.

Thus, in the scope of this change I expect to implement:

  • A new API endpoint, project.column.search, for retrieving column information.
  • That API endpoint will interact with projectPHIDs, not generic boardPHIDs.

This also does not preclude objects other than tasks (for example, Pholio mocks) from appearing on workboards in the future, although it does mean that a particular project will not be able to have one board for tasks and a separate board for mocks.

These changes are now available in HEAD of master, and should be available on stable after Jan 13. master is also safe to upgrade into right now, although some T11114 stuff may churn later this week.

In particular:


given the id of a milestone: obtain the parent projects of that milestone

  • Use project.search with the ids constraint, and examine the "parent" property in the results.
  • Or: use project.search with the ids constraint and the ancestors attachment for complete information.

Here's an example of looking up Diffusion v3, with ID 1420, on this install. Note that the ancestors array includes only a single element, because the milestone only has a single parent, but if the milestone was a grandchild or great-grandchild of some other project the entire ancestry would be included.

$ cat get-milestone-parent.json 
{
  "constraints": {
    "ids": [
      1420
    ]
  },
  "attachments": {
    "ancestors": true
  }
}
$ cat get-milestone-parent.json | arc call-conduit project.search | json_pp
{
   "errorMessage" : null,
   "error" : null,
   "response" : {
      "data" : [
         {
            "phid" : "PHID-PROJ-gqjru2g3u4c7dvbc2w4y",
            "id" : 1420,
            "type" : "PROJ",
            "fields" : {
               "depth" : 1,
               "slug" : null,
               "color" : {
                  "name" : "Blue",
                  "key" : "blue"
               },
               "parent" : {
                  "phid" : "PHID-PROJ-1ca6a71659587abf0785",
                  "id" : 9,
                  "name" : "Diffusion"
               },
               "name" : "v3",
               "icon" : {
                  "icon" : "fa-map-marker",
                  "name" : "Milestone",
                  "key" : "milestone"
               },
               "policy" : {
                  "view" : "public",
                  "edit" : "PHID-PROJ-ycm4xipi3tdsdgnuqoeg",
                  "join" : "users"
               },
               "description" : null,
               "dateCreated" : 1455659435,
               "milestone" : 1,
               "dateModified" : 1455858582
            },
            "attachments" : {
               "ancestors" : {
                  "ancestors" : [
                     {
                        "id" : 9,
                        "name" : "Diffusion",
                        "phid" : "PHID-PROJ-1ca6a71659587abf0785"
                     }
                  ]
               }
            }
         }
      ],
      "query" : {
         "queryKey" : null
      },
      "maps" : {
         "slugMap" : []
      },
      "cursor" : {
         "before" : null,
         "limit" : 100,
         "after" : null,
         "order" : null
      }
   }
}

given the id of a milestone: obtain the set of tasks assigned to that milestone via a workboard

  • Use project.search to get the milestone PHID.
  • Use maniphest.search with the projectPHIDs constraint to find tasks tagged with that milestone.

From the example above, we can see that the PHID of the "Diffusion v3" milestone is PHID-PROJ-gqjru2g3u4c7dvbc2w4y. We can then find tasks tagged with that milestone like this:

$ cat get-tasks-tagged-with-milestone.json
{
  "constraints": {
    "projects": [
      "PHID-PROJ-gqjru2g3u4c7dvbc2w4y"
    ]
  }
}
$ cat get-tasks-tagged-with-milestone.json | arc call-conduit maniphest.search | json_pp
{
   "errorMessage" : null,
   "error" : null,
   "response" : {
      "data" : [
         {
            "fields" : {
               "dateCreated" : 1455642516,
               "name" : "General support for multiple URIs for a repository",
<... snip ...>

use workboards to represent releases

I'm not completely sure what you need for this, but I think it's probably possible now:

  • You can now use the columns attachment to maniphest.search to find the columns where a task appears.
  • Use project.column.search to get more information about columns by ID or PHID, or find all the columns on a particular board by project.

Let us know if have any questions or run into issues.

epriestley closed this task as Resolved.EditedJan 9 2017, 4:54 PM
epriestley claimed this task.

I've also made these additional changes, not directly related to the original request:

  • project.search now allows you to query by parents or ancestors as constraints.
  • project.search now allows you to query by isMilestone as a constraint.
  • project.search now returns milestone, which is null for non-milestone projects or the the milestone sequence number for milestone projects. You can use this field to distinguish between milestones and non-milestones, or order milestones.
  • project.search now returns depth, which is the number of ancestors a project has. 0 for root projects, 1 for first-level subprojects, etc.
  • The API console should now guide users toward the v3 API methods (*.search, *.edit) over older methods in cases where both exist.

Some caveats:

  • There is no way to directly query for tasks by column. Instead, you can query maniphest.search with the columns attachment and the projects constraint to read all tasks on the board, then read tasks on the particular column from the results. Querying for all tasks in a column is internally complicated because we must do "board layout" before we can return a result: in particular, we must stick any tasks tagged with the project but not yet represented on the board into the appropriate "backlog" column. We may provide a better way to get this data eventually (especially if we allow objects other than tasks to appear on boards in the future) but this slightly roundabout approach is your best bet for now.
  • The data returned for columns from project.column.search is currently fairly limited, but I'm not sure what use cases exist for other fields or how to best represent the data.

These changes have also caused some minor compatibility breaks:

  • Previously, project.search and custom saved queries in the web UI never returned milestones. They now will return milestones. You can change web UI queries to use "Milestones: Hide Milestones" and use isMilestone via the API if you wanted the old behavior.
  • I've made two consistency changes opportunistically because behavior changed anyway:
    • The memberPHIDs constraint on project.search has been renamed to members.
    • The wathcerPHIDs constraint on project.search has been renamed to watchers.

Since ~30 users ended up getting pulled in here after merges, I'm going to close this as resolved. If there are followups, feel free to open new tasks describing particular issues or requests and we can pursue them more narrowly.

epriestley added a comment.EditedJan 9 2017, 5:41 PM

Oh, one final note:

Some workboard columns are "proxy" columns for a different object. Today, the only type of proxy column is milestone columns, although I expect that subprojects will also have proxy columns in the future (after T11036). These columns are rendered in a different color.

For example, if you have a project hierarchy like Starship > Warp Drive > Sprint 345, where Sprint 345 is a milestone, and have some task tagged with that milestone:

  • When you look at the workboard for "Starship", the task will not currently be visible, because it is in a subproject and subprojects do not currently generate workboard columns. They will after T11036.
  • When you look at the workboard for "Warp Drive", the task will be visible in the special milestone column for "Sprint 345". This is a "proxy column", and renders with a purple color and a milestone icon on the right-hand side of the board.
  • When you look at the workboard for "Sprint 345", the task will be in whichever normal column it has been dragged into (maybe "Backlog" or "In Progress").

The "columns" attachment for tasks does not report proxy columns, only "real" columns. For example, it will only return the "Sprint 345 (In Progress)" column in this example, even though the task is technically visible on the "Warp Drive" board as well.

If you need information about proxy columns, you can infer which proxy columns the task is part of by examining the projects it is tagged with (using the projects attachment) and the relationships between those projects (using project.search). My guess is that this information is almost never useful/interesting to API consumers and will generally just complicate and confuse things, which is why I've chosen to omit it.

Note that project.column.search does return information about proxy columns.