Skip to content
This repository has been archived by the owner on Mar 11, 2021. It is now read-only.

Area API for creating hierarchical area/sub-area in a space. #655

Merged
merged 38 commits into from Feb 14, 2017

Conversation

sbose78
Copy link
Member

@sbose78 sbose78 commented Jan 13, 2017

An Area is a hierarchical structure that belongs to a Space.

As part of this PR, I added REST capabilities to

Design decisions:

  • The datastype 'ltree' is used to store the hierarchy in the postgresql database in the Areas table.
  • The hierarchy is stored as dot-delimited list of areas for child areas.
  • The UUID is stored in the hierarchy of areas in the "Path" column in Areas table.
  • The UUID is a set of alphanumeric characters in the format xxxyyxx-yyyy-uuu3u-hjkh434 however the ltree type in postgres supports only the "" special character and not the "-" character. Hence UUIDs are converted into the 'ltree' format by converting "-" to "" and vice versa when needed.

Fixes #688 , #689 and #690

@codecov-io
Copy link

codecov-io commented Jan 13, 2017

Codecov Report

Merging #655 into master will decrease coverage by -0.02%.
The diff coverage is 71.69%.

@@            Coverage Diff            @@
##           master    #655      +/-   ##
=========================================
- Coverage   72.12%   72.1%   -0.02%     
=========================================
  Files          80      83       +3     
  Lines        4760    4972     +212     
=========================================
+ Hits         3433    3585     +152     
- Misses        996    1041      +45     
- Partials      331     346      +15
Impacted Files Coverage Δ
main.go 0% <ø> (ø)
migration/migration.go 52.5% <100%> (+0.19%)
space-areas.go 44% <44%> (ø)
area.go 76.8% <76.8%> (ø)
area/area.go 77.19% <77.19%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0fad105...ce10325. Read the comment docs.

@kwk
Copy link
Collaborator

kwk commented Jan 13, 2017

[test]

@sbose78 sbose78 force-pushed the area-service branch 2 times, most recently from 0194224 to aa19829 Compare January 23, 2017 18:52
@sbose78
Copy link
Member Author

sbose78 commented Jan 23, 2017

Scenario: Create a space and an area under it

1. Create a Space under which the areas and sub areas would be created.


$ /bin/alm-cli create space --key SOME_TOKEN  --payload '{ "data":{ "type":"spaces" ,  "attributes":{"name":"redhat-engg"}}}' -H localhost:8080 --pp

2017/01/24 03:01:02 [INFO] started id=Z96NlmWw POST=http://localhost:8080/api/spaces
2017/01/24 03:01:02 [INFO] completed id=Z96NlmWw status=201 time=6.862782ms
{
    "data": {
        "attributes": {
            "created-at": "2017-01-24T03:01:02.699539+05:30",
            "name": "redhat-engg",
            "updated-at": "2017-01-24T03:01:02.699539+05:30",
            "version": 0
        },
        "id": "41a88c1c-e2ec-48f4-b25f-d86e52eb06ea",
        "links": {
            "self": "http://localhost:8080/api/spaces/41a88c1c-e2ec-48f4-b25f-d86e52eb06ea"
        },
        "relationships": {
            "iterations": {
                "links": {
                    "related": "http://localhost:8080/api/spaces/41a88c1c-e2ec-48f4-b25f-d86e52eb06ea/iterations"
                }
            }
        },
        "type": "spaces"
    }

2. Create an area under the space created above

$ ./bin/alm-cli create space-areas --key $ALM_TOKEN --id  "f214c5d7-1166-4a05-9b43-9e82394d818c"  --payload '{ "data":{"type":"areas","attributes":{"name":"area1"} }}' -H localhost:8080 --pp
2017/02/09 00:03:18 [INFO] started id=tNzPEmy9 POST=http://localhost:8080/api/spaces/f214c5d7-1166-4a05-9b43-9e82394d818c/Areas
2017/02/09 00:03:18 [INFO] completed id=tNzPEmy9 status=201 time=30.698729ms
{
    "data": {
        "attributes": {
            "created-at": "2017-02-09T00:03:18.168278+05:30",
            "name": "area1",
            "parent_path": "/",
            "parent_path_resolved": "/",
            "version": 0
        },
        "id": "2a1ebc01-4a0a-4fd8-a79f-92ed3e562d46",
        "links": {
            "self": "http://localhost:8080/api/areas/2a1ebc01-4a0a-4fd8-a79f-92ed3e562d46"
        },
        "relationships": {
            "children": {
                "links": {
                    "self": "http://localhost:8080/api/areas/2a1ebc01-4a0a-4fd8-a79f-92ed3e562d46/children"
                }
            },
            "space": {
                "data": {
                    "id": "f214c5d7-1166-4a05-9b43-9e82394d818c",
                    "type": "spaces"
                },
                "links": {
                    "self": "http://localhost:8080/api/spaces/f214c5d7-1166-4a05-9b43-9e82394d818c"
                }
            }
        },
        "type": "areas"
    }
}

Scenario : Create area under an area, basically a sub-area.

./bin/alm-cli create space --key $ALM_TOKEN  --payload '{ "data":{ "type":"spaces" ,  "attributes":{"name":"redhat-engg2"}}}' -H localhost:8080 --pp

The json-api response looks like this.

2017/02/08 23:37:07 [INFO] started id=qAGfoiby POST=http://localhost:8080/api/spaces
2017/02/08 23:37:07 [INFO] completed id=qAGfoiby status=201 time=8.591655ms
{
    "data": {
        "attributes": {
            "created-at": "2017-02-08T23:37:07.113433+05:30",
            "description": "",
            "name": "redhat-engg2",
            "updated-at": "2017-02-08T23:37:07.113433+05:30",
            "version": 0
        },
        "id": "f214c5d7-1166-4a05-9b43-9e82394d818c",
        "links": {
            "self": "http://localhost:8080/api/spaces/f214c5d7-1166-4a05-9b43-9e82394d818c"
        },
        "relationships": {
            "iterations": {
                "links": {
                    "related": "http://localhost:8080/api/spaces/f214c5d7-1166-4a05-9b43-9e82394d818c/iterations"
                }
            }
        },
        "type": "spaces"
    }
}

In the database column "path",
the value is stored as

528bda88_f458_40bf_8e76_925cbca5960c.ceb17d71_9eea_4b4d_9c51_65814e4dfc28.ccb325e4_417c_4370_8181_f47ccc0a15db

The hierarchy is dot delimited, the uuids of the parent area(s) is stored by replacing the "-" with "_" because the "-" character is not supported in the ltree field.

** Scenario : List all children of a specific area **

@sbose78 sbose78 changed the title WIP : Area service WIP : Area API for creating hierarchical area/sub-area in a space. Jan 24, 2017
@sbose78 sbose78 force-pushed the area-service branch 3 times, most recently from 5e5b403 to e93121b Compare January 24, 2017 16:32
@sbose78
Copy link
Member Author

sbose78 commented Jan 24, 2017

[test]

@sbose78 sbose78 changed the title WIP : Area API for creating hierarchical area/sub-area in a space. Area API for creating hierarchical area/sub-area in a space. Jan 25, 2017
@sbose78 sbose78 requested a review from kwk January 25, 2017 07:24
@kwk
Copy link
Collaborator

kwk commented Jan 25, 2017

[test]

@kwk
Copy link
Collaborator

kwk commented Jan 25, 2017

[test]

area.go Outdated
spaceID := area.SpaceID.String()

selfURL := rest.AbsoluteURL(request, app.AreaHref(area.ID))
spaceSelfURL := rest.AbsoluteURL(request, "/api/spaces/"+spaceID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpaceHref ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing out - done here, 39a653f

@@ -139,6 +139,9 @@ func getMigrations() migrations {
// Version 20
m = append(m, steps{executeSQLFile("020-work-item-description-update-search-index.sql")})

// Version 21
m = append(m, steps{executeSQLFile("021-areas.sql")})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to 22?

return svc, NewSpaceAreasController(svc, rest.db)
}

func (rest *TestSpaceAreaREST) TestSuccessCreateArea() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some non happy case tests, e.g. 404 on create area in missing space. Missing auth on create..

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure @aslakknutsen - was waiting to have my approach reviewed before I did that. Thanks for the review.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added negative tests: 1104884


var areaAttributes = a.Type("AreaAttributes", func() {
a.Description(`JSONAPI store for all the "attributes" of a Area. +See also see http://jsonapi.org/format/#document-resource-object-attributes`)
a.Attribute("name", d.String, "The Area name", func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expose created-at

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expose path, but resolved by Name.

name: Child2
path: Area1.Child2.Child2

Maybe both 'path' as uuid and 'path' as resolved.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposed CreatedAt and Version ( e48559a )

Copy link
Member Author

@sbose78 sbose78 Jan 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aslakknutsen , does this mean that in the request parameter, only path would be passed as area1.child1.child2
and internally we would be persisting in the database both the name( derived ) and path ?

Maybe both 'path' as uuid and 'path' as resolved.

Didn't understand this - Could you please explain ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, the following needs to be exposed, agnostic to how we persist in the database:

  1. path ( of UUIDs upto topmost parent )
  2. path_resolved ( of area_names of the above UUIDs )
  3. relationship : link to all children
  4. relationship : link to parent.

@@ -0,0 +1,13 @@
CREATE EXTENSION IF NOT EXISTS "ltree";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 2 migration scripts?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silly me, removed.

@sbose78 sbose78 force-pushed the area-service branch 2 times, most recently from c300f31 to e8c91bb Compare February 1, 2017 02:49
defer goa.MeasureSince([]string{"goa", "db", "area", "create"}, time.Now())

u.ID = uuid.NewV4()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should default value for Path be ConvertToLltreeFormat(u.ID.String()) ?

if u.Path == ""{
u.Path = ConvertToLltreeFormat(u.ID.String())
}

Ref:- default path for base WIT is itself.

postgres=# select name,path from work_item_types where name = 'planneritem';
    name     |    path     
-------------+-------------
 planneritem | planneritem
(1 row)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback @pranavgore09 - yes we could, but the path is the parent path - the name has been renamed at the service level to parent_path..

})

// new version of "list" for migration
var _ = a.Resource("space-areas", func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a.Resource("space-area", func() {
a.BasePath("/space-areas")

for the sake of consistency?

Copy link
Member Author

@sbose78 sbose78 Feb 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your feedback @alexeykazakov ,

Sorry if I'm missing something silly and obvious !
I don't quite understand what is the advantage of doing so. I checked `design/iterations.go' and we do not add the base path mentioned by you.

./bin/alm-cli create space-areas --key $ALM_TOKEN --id  "f214c5d7-1166-4a05-9b43-9e82394d818c"  --payload '{ "data":{"type":"areas","attributes":{"name":"area1"} }}' -H localhost:8080 --pp


2017/02/08 23:37:59 [INFO] started id=yND1mdkC POST=http://localhost:8080/api/spaces/f214c5d7-1166-4a05-9b43-9e82394d818c/Areas
2017/02/08 23:37:59 [INFO] completed id=yND1mdkC status=201 time=8.446925ms
{
    "data": {
        "attributes": {
            "created-at": "2017-02-08T23:37:59.644013+05:30",
            "name": "area1",
            "parent_path": "/",
            "version": 0
        },
        "id": "256db834-a620-46b8-9322-808ee983c8a7",
        "links": {
            "self": "http://localhost:8080/api/areas/256db834-a620-46b8-9322-808ee983c8a7"
        },
        "relationships": {
            "children": {
                "links": {
                    "self": "http://localhost:8080/api/areas/256db834-a620-46b8-9322-808ee983c8a7/children"
                }
            },
            "space": {
                "data": {
                    "id": "f214c5d7-1166-4a05-9b43-9e82394d818c",
                    "type": "spaces"
                },
                "links": {
                    "self": "http://localhost:8080/api/spaces/f214c5d7-1166-4a05-9b43-9e82394d818c"
                }
            }
        },
        "type": "areas"
    }
}

The above is how the request looks like , how do you suggest we design the url paths in the API?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

./bin/alm-cli create space-area instead of ./bin/alm-cli create space-areas
as we do for space: ./bin/alm-cli create space
As I said, just for the sake of consistency ;)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexeykazakov , I was following space-iterations for consistency - If u suggest I could update stuff of space-area here and space-iteration in another PR :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. so we have more inconsistency than I though ;)

@aslakknutsen aslakknutsen merged commit 26af244 into fabric8-services:master Feb 14, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants