Merge lp:~rvb/gwacl/filtering into lp:gwacl

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: 166
Merged at revision: 166
Proposed branch: lp:~rvb/gwacl/filtering
Merge into: lp:gwacl
Diff against target: 77 lines (+49/-0)
2 files modified
management.go (+22/-0)
management_test.go (+27/-0)
To merge this branch: bzr merge lp:~rvb/gwacl/filtering
Reviewer Review Type Date Requested Status
Gavin Panella Approve
Review via email: mp+173193@code.launchpad.net

Commit message

Add the ability for ListSpecificHostedServices to filter out services if they don't match the given prefix.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

[1]

 type ListSpecificHostedServicesRequest struct {
-    ServiceNames []string
+    ServiceNames      []string
+    ServiceNamePrefix string
 }

This is kind of ugly. It's muddying the expected behaviour of
ListSpecificHostedServices. I think it also falls into the realms of
Jeroen's Swiss Army Knife antipattern.

How about allowing ServiceNames to contain globs? I don't think that
services can have glob pattern characters in them, so it should be
safe (and they can be escaped if necessary anyway).

See http://golang.org/pkg/path/#Match

Alternatively, you could split the prefix matching functionality out
into a separate ListHostedServicesWithPrefix function.

review: Needs Information
Revision history for this message
Raphaël Badin (rvb) wrote :

> [1]
>
> type ListSpecificHostedServicesRequest struct {
> -    ServiceNames []string
> +    ServiceNames      []string
> +    ServiceNamePrefix string
> }
>
> This is kind of ugly. It's muddying the expected behaviour of
> ListSpecificHostedServices. I think it also falls into the realms of
> Jeroen's Swiss Army Knife antipattern.
>
> How about allowing ServiceNames to contain globs? I don't think that
> services can have glob pattern characters in them, so it should be
> safe (and they can be escaped if necessary anyway).
>
> See http://golang.org/pkg/path/#Match
>
> Alternatively, you could split the prefix matching functionality out
> into a separate ListHostedServicesWithPrefix function.

I must say I started with a separate method but then I found that the new functionality was fitting nicely into the existing method. Can you elaborate a bit on what exactly is ugly here? The two constrains do not really step on each other's toes.

Revision history for this message
Raphaël Badin (rvb) wrote :

Maybe it's the name of the name that is not fitting any more… because if you look at that method as a filtering method which takes a set of constraints and spits out the list of services whos names match the constraints, it seems okay to me as it is in this branch. Don't you agree?

Revision history for this message
Gavin Panella (allenap) wrote :

> I must say I started with a separate method but then I found that the new
> functionality was fitting nicely into the existing method.  Can you elaborate
> a bit on what exactly is ugly here?  The two constrains do not really step on
> each other's toes.

The effect of specifying both ServiceNames and ServiceNamePrefix
overlaps. Without looking at the code or the docs, how does a
developer remember its behaviour when:

- both ServiceNames and ServiceNamePrefix are empty? Does this return
  nothing or everything?

- ServiceNames is empty, but ServiceNamePrefix is "foo"? Does this
  return nothing or everything beginning with "foo"?

- ...

If the two approaches are split into separate functions then the
behaviour becomes self-evident.

Likewise, if you were to stick to just ServiceNames and use glob
patterns the behaviour would be self-evident.

Revision history for this message
Raphaël Badin (rvb) wrote :

The glob pattern solution is the best solution to confuse people I think so I went with the original design and added a new method. I hate to add yet another ad-hoc method instead of having a generic filtering method but your point about the behavior being not self-evident without reading the doc is a good one.

lp:~rvb/gwacl/filtering updated
165. By Raphaël Badin

Add tListPrefixedHostedServices.

Revision history for this message
Gavin Panella (allenap) wrote :

Looks good. Thanks for splitting this out.

[1]

+    if request.ServiceNamePrefix != "" {
+        resServices := []HostedServiceDescriptor{}
+        for _, service := range services {
+            if strings.Index(service.ServiceName, request.ServiceNamePrefix) == 0 {
+                resServices = append(resServices, service)
+            }
+        }
+        services = resServices
+    }
+    return services, nil

This extra conditional is not really needed; it's optimisation only.
Keep it simple; at worst it will copy a slice, which is going to do as
much harm to this program as a fart would to a solar flare.

    resServices := []HostedServiceDescriptor{}
    for _, service := range services {
        if strings.HasPrefix(service.ServiceName, request.ServiceNamePrefix) {
            resServices = append(resServices, service)
        }
    }
    return resServices

Note that I've also changed it to use the HasPrefix function.

[2]

+    prefix := "prefix"
+    service1 := &HostedServiceDescriptor{ServiceName: prefix + "service1"}
+    service2 := makeHostedServiceDescriptor()

This goes back to the argument I was making about locality last week.
It's not obvious that makeHostedServiceDescriptor() creates a service
with a ServiceName that doesn't begin with `prefix`.

Also, consider dispensing with `prefix := "prefix"`. The variable is
used twice in the whole function. It would be clearer to choose a
distinctive prefix and type it in.

    service1 := &HostedServiceDescriptor{ServiceName: "foo123"}
    service2 := &HostedServiceDescriptor{ServiceName: "bar123"}
    ...
    request := &ListPrefixedHostedServicesRequest{
        ServiceNamePrefix: "foo",
    }

The term "prefix" appears eight times in that single test function as
it stands; with this change the term "foo" would appear in it exactly
twice, right where it's relevant, and it would be obvious how the two
services differ.

review: Approve
lp:~rvb/gwacl/filtering updated
166. By Raphaël Badin

Review fixes.

Revision history for this message
Raphaël Badin (rvb) wrote :

> Looks good. Thanks for splitting this out.
>
>
> [1]
>
> +    if request.ServiceNamePrefix != "" {
> +        resServices := []HostedServiceDescriptor{}
> +        for _, service := range services {
> +            if strings.Index(service.ServiceName, request.ServiceNamePrefix)
> == 0 {
> +                resServices = append(resServices, service)
> +            }
> +        }
> +        services = resServices
> +    }
> +    return services, nil
>
> This extra conditional is not really needed; it's optimisation only.
> Keep it simple; at worst it will copy a slice, which is going to do as
> much harm to this program as a fart would to a solar flare.
>
>    resServices := []HostedServiceDescriptor{}
>    for _, service := range services {
>        if strings.HasPrefix(service.ServiceName, request.ServiceNamePrefix) {
>            resServices = append(resServices, service)
>        }
>    }
>    return resServices
>
> Note that I've also changed it to use the HasPrefix function.

All right, fixed.

> [2]
>
> +    prefix := "prefix"
> +    service1 := &HostedServiceDescriptor{ServiceName: prefix + "service1"}
> +    service2 := makeHostedServiceDescriptor()
>
> This goes back to the argument I was making about locality last week.
> It's not obvious that makeHostedServiceDescriptor() creates a service
> with a ServiceName that doesn't begin with `prefix`.
>
> Also, consider dispensing with `prefix := "prefix"`. The variable is
> used twice in the whole function. It would be clearer to choose a
> distinctive prefix and type it in.
>
>    service1 := &HostedServiceDescriptor{ServiceName: "foo123"}
>    service2 := &HostedServiceDescriptor{ServiceName: "bar123"}
>    ...
>    request := &ListPrefixedHostedServicesRequest{
>        ServiceNamePrefix: "foo",
>    }
>
> The term "prefix" appears eight times in that single test function as
> it stands; with this change the term "foo" would appear in it exactly
> twice, right where it's relevant, and it would be obvious how the two
> services differ.

Yeah, well, we're going to have to agree to disagree on this one :). Also, grep -c prefix management_test.go -> 3 (!= 8)

Revision history for this message
Gavin Panella (allenap) wrote :

> Yeah, well, we're going to have to agree to disagree on this one :).

Yeah, I'm fine with that... I'll keep trying to convince you though :)

> Also, grep -c prefix management_test.go -> 3 (!= 8)

Okay, firstly, my mistake. The term "prefix" *without regard to case*
appears 8 times. Secondly, grep -c counts matching lines, not matches.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'management.go'
2--- management.go 2013-07-05 09:28:09 +0000
3+++ management.go 2013-07-08 07:03:25 +0000
4@@ -5,6 +5,7 @@
5
6 import (
7 "sort"
8+ "strings"
9 )
10
11 type ListInstancesRequest struct {
12@@ -96,6 +97,27 @@
13 return services, nil
14 }
15
16+type ListPrefixedHostedServicesRequest struct {
17+ ServiceNamePrefix string
18+}
19+
20+// ListPrefixedHostedServices returns a slice containing specific
21+// HostedServiceDescriptor objects, insofar as they match the request.
22+func (api *ManagementAPI) ListPrefixedHostedServices(request *ListPrefixedHostedServicesRequest) ([]HostedServiceDescriptor, error) {
23+ services, err := api.ListHostedServices()
24+ if err != nil {
25+ return nil, err
26+ }
27+ resServices := []HostedServiceDescriptor{}
28+ for _, service := range services {
29+ if strings.HasPrefix(service.ServiceName, request.ServiceNamePrefix) {
30+ resServices = append(resServices, service)
31+ }
32+ }
33+ services = resServices
34+ return services, nil
35+}
36+
37 type DestroyDeploymentRequest struct {
38 ServiceName string
39 DeploymentName string
40
41=== modified file 'management_test.go'
42--- management_test.go 2013-07-05 10:47:55 +0000
43+++ management_test.go 2013-07-08 07:03:25 +0000
44@@ -195,6 +195,33 @@
45 c.Assert(recordedRequests, HasLen, 1)
46 }
47
48+func (suite *managementAPISuite) TestListPrefixedHostedServices(c *C) {
49+ prefix := "prefix"
50+ service1 := &HostedServiceDescriptor{ServiceName: prefix + "service1"}
51+ service2 := makeHostedServiceDescriptor()
52+ list := HostedServiceDescriptorList{HostedServices: []HostedServiceDescriptor{*service1, *service2}}
53+ XML, err := list.Serialize()
54+ c.Assert(err, IsNil)
55+ fixedResponse := x509Response{
56+ StatusCode: http.StatusOK,
57+ Body: []byte(XML),
58+ }
59+ rigFixedResponseDispatcher(&fixedResponse)
60+ recordedRequests := make([]*X509Request, 0)
61+ rigRecordingDispatcher(&recordedRequests)
62+
63+ api := makeAPI(c)
64+ request := &ListPrefixedHostedServicesRequest{
65+ ServiceNamePrefix: prefix,
66+ }
67+ descriptors, err := api.ListPrefixedHostedServices(request)
68+
69+ // Only the first service is returned.
70+ c.Check(descriptors, DeepEquals, []HostedServiceDescriptor{*service1})
71+ // Only one request to the API is made.
72+ c.Assert(recordedRequests, HasLen, 1)
73+}
74+
75 func (suite *managementAPISuite) TestListSpecificHostedServicesWithoutNamesReturnsNothing(c *C) {
76 service1 := makeHostedServiceDescriptor()
77 service2 := makeHostedServiceDescriptor()

Subscribers

People subscribed via source and target branches

to all changes: