Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for sub-indexes #120

Merged
merged 5 commits into from
Oct 18, 2023
Merged

Add support for sub-indexes #120

merged 5 commits into from
Oct 18, 2023

Conversation

abhinav
Copy link
Collaborator

@abhinav abhinav commented Oct 15, 2023

Background

Sally renders two kinds of pages:

  • packages: These are for packages defined in sally.yaml
    and any route under the package path.
  • indexes: These list available packages.

The latter--indexes was previously only supported at '/', the root page.

This leads to a slight UX issue:
if you have a package with a / in its name (e.g. net/metrics):

  • example.com/net/metrics gives you the package page
  • example.com/ lists net/metrics
  • However, example.com/net fails with a 404

This adds support for index pages on all parents of package pages.
Therefore, if example.com/net/metrics exists,
example.com/net will list all packages defined under that path.

Resolves #31

<details>
<summary>Background</summary>

Sally renders two kinds of pages:

- packages: These are for packages defined in sally.yaml
  and any route under the package path.
- indexes: These list available packages.

The latter--indexes was previously only supported at '/', the root page.

This leads to a slight UX issue:
if you have a package with a / in its name (e.g. net/metrics):

- example.com/net/metrics gives you the package page
- example.com/ lists net/metrics
- However, example.com/net fails with a 404

</details>

This adds support for index pages on all parents of package pages.
Therefore, if example.com/net/metrics exists,
example.com/net will list all packages defined under that path.

To make this possible, the internals have been rewritten to some degree:
CreateHandler no longer builds an http.ServeMux
with a bunch of pre-registered http.Handlers.
It now builds a custom http.Handler with its own routing.

Routing is backed by a new pathTree abstraction.
pathTree is a specialized string-value storage
with support for inheriting values from a parent.
It has the following methods:

- `Set(path, v)` associates v with path.
- `Lookup(path)` returns the value at path,
  or the value inherited from the closest ancestor.
- `ListByPath(path)` lists all values at or under the given path.

For additional confidence in the implementation of pathTree,
a property test based on rapid is included.

Resolves uber-go#31
@codecov
Copy link

codecov bot commented Oct 15, 2023

Codecov Report

Merging #120 (322469f) into master (f3a3b27) will increase coverage by 4.86%.
The diff coverage is 96.20%.

@@            Coverage Diff             @@
##           master     #120      +/-   ##
==========================================
+ Coverage   74.03%   78.90%   +4.86%     
==========================================
  Files           3        3              
  Lines         104      128      +24     
==========================================
+ Hits           77      101      +24     
  Misses         23       23              
  Partials        4        4              
Files Coverage Δ
handler.go 93.54% <96.20%> (+2.24%) ⬆️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

Switch back to http.ServeMux for package handlers,
and move the scanning logic to the index handler.
@abhinav
Copy link
Collaborator Author

abhinav commented Oct 17, 2023

The implementation has been simplified significantly.
I've eliminated the custom routing backend in favor of the same old ServeMux-backed router.
The performance difference from the custom router was non-existent and the cost in terms of complexity was a lot higher.

Copy link
Member

@mway mway left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines 36 to +37
mux := http.NewServeMux()

pkgs := make([]packageInfo, 0, len(config.Packages))
pkgs := make([]*sallyPackage, 0, len(config.Packages))
Copy link
Member

Choose a reason for hiding this comment

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

nit: var group

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree with var grouping when var form is used, but I think when we're initializing with non-zero values, var form is rarely used.

Per the style guide, we use var form for zero value initialization and := form for non-zero-value:
https://github.com/uber-go/guide/blob/master/style.md#use-var-for-zero-value-structs
https://github.com/uber-go/guide/blob/master/style.md#initializing-struct-references

(That links only talk about structs, but the guidance has been consistent on this.)

The readability in a var group suffers as soon as you add a type for any actual var-form-zero-values.

var (
  mux           = http.NewServeMux()
  pkgs          = make(...)
  foo io.Reader
)

Copy link
Member

Choose a reason for hiding this comment

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

It's fine as-is. :) Not trying to bikeshed.

My point was more that I don't personally see the value in segmenting the initializations here so I would probably group them all. I disagree about var group readability suffering when mixing initialization and zero-value declarations (and I think that ultimately leads to a lot more ad hoc initialization/declaration patterns).

Comment on lines +44 to +53
modulePath := path.Join(baseURL, name)
docURL := "https://" + path.Join(config.Godoc.Host, modulePath)

pkg := &sallyPackage{
Name: name,
Desc: pkg.Desc,
ModulePath: modulePath,
DocURL: docURL,
GitURL: pkg.Repo,
}
Copy link
Member

Choose a reason for hiding this comment

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

nit: grouping

Suggested change
modulePath := path.Join(baseURL, name)
docURL := "https://" + path.Join(config.Godoc.Host, modulePath)
pkg := &sallyPackage{
Name: name,
Desc: pkg.Desc,
ModulePath: modulePath,
DocURL: docURL,
GitURL: pkg.Repo,
}
var (
modulePath = path.Join(baseURL, name)
docURL = "https://" + path.Join(config.Godoc.Host, modulePath)
pkg = &sallyPackage{
Name: name,
Desc: pkg.Desc,
ModulePath: modulePath,
DocURL: docURL,
GitURL: pkg.Repo,
}
)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Same comment as above. The var form is just less readable here. := for non-zero value initialization has always been the way.

handler.go Outdated Show resolved Hide resolved
handler_test.go Outdated Show resolved Hide resolved
Copy link
Collaborator Author

@abhinav abhinav left a comment

Choose a reason for hiding this comment

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

Thanks for the reviews, @mway @sywhang.
I've applied some suggestions, and left comments on a couple.
Let me know what you think.

Comment on lines 36 to +37
mux := http.NewServeMux()

pkgs := make([]packageInfo, 0, len(config.Packages))
pkgs := make([]*sallyPackage, 0, len(config.Packages))
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree with var grouping when var form is used, but I think when we're initializing with non-zero values, var form is rarely used.

Per the style guide, we use var form for zero value initialization and := form for non-zero-value:
https://github.com/uber-go/guide/blob/master/style.md#use-var-for-zero-value-structs
https://github.com/uber-go/guide/blob/master/style.md#initializing-struct-references

(That links only talk about structs, but the guidance has been consistent on this.)

The readability in a var group suffers as soon as you add a type for any actual var-form-zero-values.

var (
  mux           = http.NewServeMux()
  pkgs          = make(...)
  foo io.Reader
)

Comment on lines +44 to +53
modulePath := path.Join(baseURL, name)
docURL := "https://" + path.Join(config.Godoc.Host, modulePath)

pkg := &sallyPackage{
Name: name,
Desc: pkg.Desc,
ModulePath: modulePath,
DocURL: docURL,
GitURL: pkg.Repo,
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Same comment as above. The var form is just less readable here. := for non-zero value initialization has always been the way.

@abhinav
Copy link
Collaborator Author

abhinav commented Oct 18, 2023

Per chat with @mway, will leave := initialization in place for cases where var form isn't used because a type isn't specified.

@abhinav abhinav merged commit 5fbb57e into uber-go:master Oct 18, 2023
5 checks passed
@abhinav abhinav deleted the subindex branch October 18, 2023 22:18
@mway mway mentioned this pull request Oct 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for sub-indexes
3 participants