Skip to content

Commit

Permalink
Support related resources for draft documents too
Browse files Browse the repository at this point in the history
  • Loading branch information
jfreda committed Jul 11, 2023
1 parent b1e2162 commit 1b8defd
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 39 deletions.
34 changes: 19 additions & 15 deletions internal/api/documents.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,6 @@ type DocumentPatchRequest struct {
TargetVersion string `json:"targetVersion,omitempty"`
}

var (
documentsResourceURLPathRE = regexp.MustCompile(
`^\/api\/v1\/documents\/([0-9A-Za-z_\-]+)$`)
documentsResourceRelatedResourcesURLPathRE = regexp.MustCompile(
`^\/api\/v1\/documents\/([0-9A-Za-z_\-]+)\/related-resources$`)
)

func DocumentHandler(
cfg *config.Config,
l hclog.Logger,
Expand All @@ -57,7 +50,8 @@ func DocumentHandler(

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Parse document ID from the URL path.
docID, isRelatedResourcesRequest, err := parseDocumentsURLPath(r.URL.Path)
docID, isRelatedResourcesRequest, err := parseDocumentsURLPath(
r.URL.Path, "documents")
if err != nil {
l.Error("error parsing documents URL path",
"error", err,
Expand Down Expand Up @@ -491,28 +485,38 @@ func updateRecentlyViewedDocs(

// parseDocumentsURLPath parses the document ID from a documents API URL path
// and determines if it is a related resources request.
func parseDocumentsURLPath(path string) (
// resourceType should be "documents" or "drafts", as appropriate.
func parseDocumentsURLPath(path, resourceType string) (
docID string,
isRelatedResourcesRequest bool,
err error,
) {
resourceURLPathRE := regexp.MustCompile(
fmt.Sprintf(
`^\/api\/v1\/%s\/([0-9A-Za-z_\-]+)$`,
resourceType))
resourceRelatedResourcesURLPathRE := regexp.MustCompile(
fmt.Sprintf(
`^\/api\/v1\/%s\/([0-9A-Za-z_\-]+)\/related-resources$`,
resourceType))

switch {
case documentsResourceURLPathRE.MatchString(path):
matches := documentsResourceURLPathRE.FindStringSubmatch(path)
case resourceURLPathRE.MatchString(path):
matches := resourceURLPathRE.FindStringSubmatch(path)
if len(matches) != 2 {
return "", false, fmt.Errorf(
"wrong number of string submatches for documents resource URL path")
"wrong number of string submatches for resource URL path")
}
return matches[1], false, nil

case documentsResourceRelatedResourcesURLPathRE.MatchString(path):
matches := documentsResourceRelatedResourcesURLPathRE.
case resourceRelatedResourcesURLPathRE.MatchString(path):
matches := resourceRelatedResourcesURLPathRE.
FindStringSubmatch(path)
if len(matches) != 2 {
return "",
true,
fmt.Errorf(
"wrong number of string submatches for documents resource related resources URL path")
"wrong number of string submatches for resource related resources URL path")
}
return matches[1], true, nil

Expand Down
21 changes: 8 additions & 13 deletions internal/api/documents_related_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"net/http"

"github.com/algolia/algoliasearch-client-go/v3/algolia/errs"
"github.com/hashicorp-forge/hermes/pkg/algolia"
hcd "github.com/hashicorp-forge/hermes/pkg/hashicorpdocs"
"github.com/hashicorp-forge/hermes/pkg/models"
Expand Down Expand Up @@ -238,18 +237,15 @@ func documentsResourceRelatedResourcesHandler(
}
}

func getDocumentFromAlgolia(docID string, algo *algolia.Client) (hcd.Doc, error) {
// getDocumentFromAlgolia gets a document object from Algolia.
func getDocumentFromAlgolia(
docID string, algo *algolia.Client) (hcd.Doc, error) {

// Get base document object from Algolia so we can determine the doc type.
baseDocObj := &hcd.BaseDoc{}
err := algo.Docs.GetObject(docID, &baseDocObj)
if err != nil {
// Handle 404 from Algolia and only log a warning.
if _, is404 := errs.IsAlgoliaErrWithCode(err, 404); is404 {
return nil, fmt.Errorf("base document object not found")
} else {
return nil, fmt.Errorf(
"error requesting base document object from Algolia: %w", err)
}
if err := algo.Docs.GetObject(docID, &baseDocObj); err != nil {
return nil, fmt.Errorf(
"error retrieving base document object from Algolia: %w", err)
}

// Create new document object of the proper doc type.
Expand All @@ -259,8 +255,7 @@ func getDocumentFromAlgolia(docID string, algo *algolia.Client) (hcd.Doc, error)
}

// Get document object from Algolia.
err = algo.Docs.GetObject(docID, &docObj)
if err != nil {
if err := algo.Docs.GetObject(docID, &docObj); err != nil {
return nil, fmt.Errorf("error retrieving document object from Algolia")
}

Expand Down
30 changes: 23 additions & 7 deletions internal/api/documents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,49 @@ import (
func TestParseDocumentsURLPath(t *testing.T) {
cases := map[string]struct {
url string
resourceType string
wantDocID string
wantRelatedResourceRequest bool
shouldErr bool
}{
"good document resource URL": {
url: "/api/v1/documents/doc123",
wantDocID: "doc123",
url: "/api/v1/documents/doc123",
resourceType: "documents",
wantDocID: "doc123",
},
"good document resource URL with related resources": {
url: "/api/v1/documents/doc123/related-resources",
resourceType: "documents",
wantDocID: "doc123",
wantRelatedResourceRequest: true,
},
"good draft resource URL": {
url: "/api/v1/drafts/doc123",
resourceType: "drafts",
wantDocID: "doc123",
},
"good draft resource URL with related resources": {
url: "/api/v1/drafts/doc123/related-resources",
resourceType: "drafts",
wantDocID: "doc123",
wantRelatedResourceRequest: true,
},
"extra frontslash after related-resources": {
url: "/api/v1/documents/doc123/related-resources/",
shouldErr: true,
url: "/api/v1/documents/doc123/related-resources/",
resourceType: "documents",
shouldErr: true,
},
"no document resource ID": {
url: "/api/v1/documents/",
shouldErr: true,
url: "/api/v1/documents/",
resourceType: "documents",
shouldErr: true,
},
}

for name, c := range cases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
docID, rrReq, err := parseDocumentsURLPath(c.url)
docID, rrReq, err := parseDocumentsURLPath(c.url, c.resourceType)

if c.shouldErr {
assert.Error(err)
Expand Down
17 changes: 13 additions & 4 deletions internal/api/drafts.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,14 +439,16 @@ func DraftsDocumentHandler(
db *gorm.DB) http.Handler {

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Get document ID from URL path
docId, err := parseURLPath(r.URL.Path, "/api/v1/drafts")
// Parse document ID from the URL path.
docId, isRelatedResourcesRequest, err := parseDocumentsURLPath(
r.URL.Path, "drafts")
if err != nil {
l.Error("error requesting document draft from algolia",
l.Error("error parsing drafts URL path",
"error", err,
"path", r.URL.Path,
"method", r.Method,
)
http.Error(w, "Error requesting document draft", http.StatusInternalServerError)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

Expand Down Expand Up @@ -519,6 +521,13 @@ func DraftsDocumentHandler(
return
}

// Pass request off to the documents related resources handler if
// appropriate.
if isRelatedResourcesRequest {
documentsResourceRelatedResourcesHandler(w, r, docId, docObj, l, ar, db)
return
}

switch r.Method {
case "GET":
now := time.Now()
Expand Down

0 comments on commit 1b8defd

Please sign in to comment.