WIP
This commit is contained in:
@@ -83,6 +83,7 @@ require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/rabbitmq/amqp091-go v1.10.0
|
||||
github.com/repeale/fp-go v0.11.1
|
||||
github.com/sassoftware/go-rpmutils v0.4.0
|
||||
github.com/schollz/progressbar/v3 v3.17.1
|
||||
github.com/sendgrid/sendgrid-go v3.16.0+incompatible
|
||||
|
||||
@@ -146,7 +146,7 @@ func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk, _ .
|
||||
if err = json.Unmarshal(contents, &env); err != nil {
|
||||
return err
|
||||
}
|
||||
s.scanVariableData(ctx, chunksChan, Metadata{EnvironmentID: env.Id, EnvironmentName: env.Name, fromLocal: true, Link: envPath, LocationType: source_metadatapb.PostmanLocationType_ENVIRONMENT_VARIABLE}, env)
|
||||
s.scanVariableData(ctx, chunksChan, Metadata{EnvironmentId: env.Id, EnvironmentName: env.Name, fromLocal: true, Link: envPath, LocationType: source_metadatapb.PostmanLocationType_ENVIRONMENT_VARIABLE}, env)
|
||||
}
|
||||
|
||||
// Scan local collections
|
||||
@@ -235,8 +235,8 @@ func (s *Source) scanLocalWorkspace(ctx context.Context, chunksChan chan *source
|
||||
s.resetKeywords()
|
||||
|
||||
metadata := Metadata{
|
||||
WorkspaceUUID: workspace.Id,
|
||||
fromLocal: true,
|
||||
WorkspaceId: workspace.Id,
|
||||
fromLocal: true,
|
||||
}
|
||||
|
||||
for _, environment := range workspace.EnvironmentsRaw {
|
||||
@@ -246,7 +246,7 @@ func (s *Source) scanLocalWorkspace(ctx context.Context, chunksChan chan *source
|
||||
metadata.LocationType = source_metadatapb.PostmanLocationType_UNKNOWN_POSTMAN
|
||||
}
|
||||
for _, collection := range workspace.CollectionsRaw {
|
||||
metadata.Link = strings.TrimSuffix(path.Base(filePath), path.Ext(filePath)) + "/collections/" + collection.Info.PostmanID + ".json"
|
||||
metadata.Link = strings.TrimSuffix(path.Base(filePath), path.Ext(filePath)) + "/collections/" + collection.Info.Id + ".json"
|
||||
s.scanCollection(ctx, chunksChan, metadata, collection)
|
||||
}
|
||||
}
|
||||
@@ -258,7 +258,7 @@ func (s *Source) scanWorkspace(ctx context.Context, chunksChan chan *sources.Chu
|
||||
|
||||
// initiate metadata to track the tree structure of postman data
|
||||
metadata := Metadata{
|
||||
WorkspaceUUID: workspace.Id,
|
||||
WorkspaceId: workspace.Id,
|
||||
WorkspaceName: workspace.Name,
|
||||
CreatedBy: workspace.CreatedBy,
|
||||
Type: "workspace",
|
||||
@@ -266,21 +266,21 @@ func (s *Source) scanWorkspace(ctx context.Context, chunksChan chan *sources.Chu
|
||||
|
||||
// gather and scan environment variables
|
||||
for _, envID := range workspace.Environments {
|
||||
envVars, err := s.client.GetEnvironmentVariables(ctx, envID.Uid)
|
||||
envVars, err := s.client.GetEnvironmentVariables(ctx, envID.Id)
|
||||
if err != nil {
|
||||
ctx.Logger().Error(err, "could not get env variables", "environment_uuid", envID.Uid)
|
||||
ctx.Logger().Error(err, "could not get env variables", "environment_id", envID.Id)
|
||||
continue
|
||||
}
|
||||
if shouldSkip(envID.Uid, s.conn.IncludeEnvironments, s.conn.ExcludeEnvironments) {
|
||||
if shouldSkip(envID.Id, s.conn.IncludeEnvironments, s.conn.ExcludeEnvironments) {
|
||||
continue
|
||||
}
|
||||
metadata.Type = ENVIRONMENT_TYPE
|
||||
metadata.Link = LINK_BASE_URL + "environments/" + envID.Uid
|
||||
metadata.FullID = envVars.Id
|
||||
metadata.EnvironmentID = envID.Uid
|
||||
metadata.Link = LINK_BASE_URL + "environments/" + envID.Id
|
||||
metadata.Id = envVars.Id
|
||||
metadata.EnvironmentId = envID.Id
|
||||
metadata.EnvironmentName = envVars.Name
|
||||
|
||||
ctx.Logger().V(2).Info("scanning environment vars", "environment_uuid", metadata.FullID)
|
||||
ctx.Logger().V(2).Info("scanning environment vars", "environment_id", metadata.Id)
|
||||
for _, word := range strings.Split(envVars.Name, " ") {
|
||||
s.attemptToAddKeyword(word)
|
||||
}
|
||||
@@ -289,10 +289,10 @@ func (s *Source) scanWorkspace(ctx context.Context, chunksChan chan *sources.Chu
|
||||
metadata.LocationType = source_metadatapb.PostmanLocationType_UNKNOWN_POSTMAN
|
||||
metadata.Type = ""
|
||||
metadata.Link = ""
|
||||
metadata.FullID = ""
|
||||
metadata.EnvironmentID = ""
|
||||
metadata.Id = ""
|
||||
metadata.EnvironmentId = ""
|
||||
metadata.EnvironmentName = ""
|
||||
ctx.Logger().V(2).Info("finished scanning environment vars", "environment_uuid", metadata.FullID)
|
||||
ctx.Logger().V(2).Info("finished scanning environment vars", "environment_id", metadata.Id)
|
||||
}
|
||||
ctx.Logger().V(2).Info("finished scanning environments")
|
||||
|
||||
@@ -300,10 +300,10 @@ func (s *Source) scanWorkspace(ctx context.Context, chunksChan chan *sources.Chu
|
||||
// at this point we have all the possible
|
||||
// substitutions from Environment variables
|
||||
for _, collectionID := range workspace.Collections {
|
||||
if shouldSkip(collectionID.Uid, s.conn.IncludeCollections, s.conn.ExcludeCollections) {
|
||||
if shouldSkip(collectionID.Id, s.conn.IncludeCollections, s.conn.ExcludeCollections) {
|
||||
continue
|
||||
}
|
||||
collection, err := s.client.GetCollection(ctx, collectionID.Uid)
|
||||
collection, err := s.client.GetCollection(ctx, collectionID.Id)
|
||||
if err != nil {
|
||||
// Log and move on, because sometimes the Postman API seems to give us collection IDs
|
||||
// that we don't have access to, so we don't want to kill the scan because of it.
|
||||
@@ -318,14 +318,14 @@ func (s *Source) scanWorkspace(ctx context.Context, chunksChan chan *sources.Chu
|
||||
// scanCollection scans a collection and all its items, folders, and requests.
|
||||
// locally scoped Metadata is updated as we drill down into the collection.
|
||||
func (s *Source) scanCollection(ctx context.Context, chunksChan chan *sources.Chunk, metadata Metadata, collection Collection) {
|
||||
ctx.Logger().V(2).Info("starting to scan collection", "collection_name", collection.Info.Name, "collection_uuid", collection.Info.Uid)
|
||||
ctx.Logger().V(2).Info("starting to scan collection", "collection_name", collection.Info.Name, "collection_id", collection.Info.Id)
|
||||
metadata.CollectionInfo = collection.Info
|
||||
metadata.Type = COLLECTION_TYPE
|
||||
s.attemptToAddKeyword(collection.Info.Name)
|
||||
|
||||
if !metadata.fromLocal {
|
||||
metadata.FullID = metadata.CollectionInfo.Uid
|
||||
metadata.Link = LINK_BASE_URL + COLLECTION_TYPE + "/" + metadata.FullID
|
||||
metadata.Id = metadata.CollectionInfo.Id
|
||||
metadata.Link = LINK_BASE_URL + COLLECTION_TYPE + "/" + metadata.Id
|
||||
}
|
||||
|
||||
metadata.LocationType = source_metadatapb.PostmanLocationType_COLLECTION_VARIABLE
|
||||
@@ -361,37 +361,38 @@ func (s *Source) scanItem(ctx context.Context, chunksChan chan *sources.Chunk, c
|
||||
metadata.FolderName = item.Name
|
||||
}
|
||||
|
||||
if item.Uid != "" {
|
||||
metadata.FullID = item.Uid
|
||||
metadata.Link = LINK_BASE_URL + FOLDER_TYPE + "/" + metadata.FullID
|
||||
if item.Id != "" {
|
||||
metadata.Id = item.Id
|
||||
metadata.Link = LINK_BASE_URL + FOLDER_TYPE + "/" + metadata.Id
|
||||
}
|
||||
// recurse through the folders
|
||||
for _, subItem := range item.Items {
|
||||
s.scanItem(ctx, chunksChan, collection, metadata, subItem, item.Uid)
|
||||
s.scanItem(ctx, chunksChan, collection, metadata, subItem, item.Id)
|
||||
}
|
||||
|
||||
// The assignment of the folder ID to be the current item UID is due to wanting to assume that your current item is a folder unless you have request data inside of your item.
|
||||
// If your current item is a folder, you will want the folder ID to match the UID of the current item.
|
||||
// If your current item is a request, you will want the folder ID to match the UID of the parent folder.
|
||||
// The assignment of the folder ID to be the current item ID is due to wanting to assume that your current item
|
||||
// is a folder unless you have request data inside of your item.
|
||||
// If your current item is a folder, you will want the folder ID to match the ID of the current item.
|
||||
// If your current item is a request, you will want the folder ID to match the ID of the parent folder.
|
||||
// If the request is at the root of a collection and has no parent folder, the folder ID will be empty.
|
||||
metadata.FolderID = item.Uid
|
||||
metadata.FolderId = item.Id
|
||||
// check if there are any requests in the folder
|
||||
if item.Request.Method != "" {
|
||||
metadata.FolderName = strings.Replace(metadata.FolderName, (" > " + item.Name), "", -1)
|
||||
metadata.FolderID = parentItemId
|
||||
if metadata.FolderID == "" {
|
||||
metadata.FolderId = parentItemId
|
||||
if metadata.FolderId == "" {
|
||||
metadata.FolderName = ""
|
||||
}
|
||||
metadata.RequestID = item.Uid
|
||||
metadata.RequestId = item.Id
|
||||
metadata.RequestName = item.Name
|
||||
metadata.Type = REQUEST_TYPE
|
||||
if item.Uid != "" {
|
||||
if item.Id != "" {
|
||||
// Route to API endpoint
|
||||
metadata.FullID = item.Uid
|
||||
metadata.Link = LINK_BASE_URL + REQUEST_TYPE + "/" + item.Uid
|
||||
metadata.Id = item.Id
|
||||
metadata.Link = LINK_BASE_URL + REQUEST_TYPE + "/" + item.Id
|
||||
} else {
|
||||
// Route to collection.json
|
||||
metadata.FullID = item.Id
|
||||
metadata.Id = item.Id
|
||||
}
|
||||
s.scanHTTPRequest(ctx, chunksChan, metadata, item.Request)
|
||||
}
|
||||
@@ -415,7 +416,7 @@ func (s *Source) scanEvent(ctx context.Context, chunksChan chan *sources.Chunk,
|
||||
|
||||
// Prep direct links. Ignore updating link if it's a local JSON file
|
||||
if !metadata.fromLocal {
|
||||
metadata.Link = LINK_BASE_URL + (strings.Replace(metadata.Type, " > event", "", -1)) + "/" + metadata.FullID
|
||||
metadata.Link = LINK_BASE_URL + (strings.Replace(metadata.Type, " > event", "", -1)) + "/" + metadata.Id
|
||||
if event.Listen == "prerequest" {
|
||||
metadata.Link += "?tab=pre-request-scripts"
|
||||
} else {
|
||||
@@ -624,9 +625,9 @@ func (s *Source) scanRequestBody(ctx context.Context, chunksChan chan *sources.C
|
||||
}
|
||||
|
||||
func (s *Source) scanHTTPResponse(ctx context.Context, chunksChan chan *sources.Chunk, m Metadata, response Response) {
|
||||
if response.Uid != "" {
|
||||
m.Link = LINK_BASE_URL + "example/" + response.Uid
|
||||
m.FullID = response.Uid
|
||||
if response.Id != "" {
|
||||
m.Link = LINK_BASE_URL + "example/" + response.Id
|
||||
m.Id = response.Id
|
||||
}
|
||||
originalType := m.Type
|
||||
|
||||
@@ -664,7 +665,7 @@ func (s *Source) scanHTTPResponse(ctx context.Context, chunksChan chan *sources.
|
||||
|
||||
func (s *Source) scanVariableData(ctx context.Context, chunksChan chan *sources.Chunk, m Metadata, variableData VariableData) {
|
||||
if len(variableData.KeyValues) == 0 {
|
||||
ctx.Logger().V(2).Info("no variables to scan", "type", m.Type, "item_uuid", m.FullID)
|
||||
ctx.Logger().V(2).Info("no variables to scan", "type", m.Type, "item_id", m.Id)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -700,7 +701,7 @@ func (s *Source) scanVariableData(ctx context.Context, chunksChan chan *sources.
|
||||
|
||||
func (s *Source) scanData(ctx context.Context, chunksChan chan *sources.Chunk, data string, metadata Metadata) {
|
||||
if data == "" {
|
||||
ctx.Logger().V(3).Info("Data string is empty", "workspace_id", metadata.WorkspaceUUID)
|
||||
ctx.Logger().V(3).Info("Data string is empty", "workspace_id", metadata.WorkspaceId)
|
||||
return
|
||||
}
|
||||
if metadata.FieldType == "" {
|
||||
@@ -718,15 +719,15 @@ func (s *Source) scanData(ctx context.Context, chunksChan chan *sources.Chunk, d
|
||||
Data: &source_metadatapb.MetaData_Postman{
|
||||
Postman: &source_metadatapb.Postman{
|
||||
Link: metadata.Link,
|
||||
WorkspaceUuid: metadata.WorkspaceUUID,
|
||||
WorkspaceUuid: metadata.WorkspaceId,
|
||||
WorkspaceName: metadata.WorkspaceName,
|
||||
CollectionId: metadata.CollectionInfo.Uid,
|
||||
CollectionId: metadata.CollectionInfo.Id,
|
||||
CollectionName: metadata.CollectionInfo.Name,
|
||||
EnvironmentId: metadata.EnvironmentID,
|
||||
EnvironmentId: metadata.EnvironmentId,
|
||||
EnvironmentName: metadata.EnvironmentName,
|
||||
RequestId: metadata.RequestID,
|
||||
RequestId: metadata.RequestId,
|
||||
RequestName: metadata.RequestName,
|
||||
FolderId: metadata.FolderID,
|
||||
FolderId: metadata.FolderId,
|
||||
FolderName: metadata.FolderName,
|
||||
FieldType: metadata.FieldType,
|
||||
LocationType: metadata.LocationType,
|
||||
@@ -776,11 +777,11 @@ func unpackWorkspace(workspacePath string) (Workspace, error) {
|
||||
return workspace, nil
|
||||
}
|
||||
|
||||
func shouldSkip(uuid string, include []string, exclude []string) bool {
|
||||
if slices.Contains(exclude, uuid) {
|
||||
func shouldSkip(object_id string, include []string, exclude []string) bool {
|
||||
if slices.Contains(exclude, object_id) {
|
||||
return true
|
||||
}
|
||||
if len(include) > 0 && !slices.Contains(include, uuid) {
|
||||
if len(include) > 0 && !slices.Contains(include, object_id) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -22,25 +22,24 @@ const (
|
||||
)
|
||||
|
||||
type Workspace struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Visibility string `json:"visibility"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
Collections []IdNameUid `json:"collections"`
|
||||
Environments []IdNameUid `json:"environments"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Visibility string `json:"visibility"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
Collections []IdName `json:"collections"`
|
||||
Environments []IdName `json:"environments"`
|
||||
CollectionsRaw []Collection
|
||||
EnvironmentsRaw []VariableData
|
||||
}
|
||||
|
||||
type IdNameUid struct {
|
||||
type IdName struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Uid string `json:"uid"`
|
||||
}
|
||||
|
||||
type KeyValue struct {
|
||||
@@ -53,7 +52,7 @@ type KeyValue struct {
|
||||
}
|
||||
|
||||
type VariableData struct {
|
||||
Id string `json:"id"` // For globals and envs, this is just the UUID, not the full ID.
|
||||
Id string `json:"id"` // UUID of the folder (but not full ID/UID)
|
||||
Name string `json:"name"`
|
||||
KeyValues []KeyValue `json:"values"`
|
||||
Owner string `json:"owner"`
|
||||
@@ -67,16 +66,16 @@ type Environment struct {
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
WorkspaceUUID string
|
||||
WorkspaceId string
|
||||
WorkspaceName string
|
||||
CreatedBy string
|
||||
EnvironmentID string
|
||||
EnvironmentId string
|
||||
CollectionInfo Info
|
||||
FolderID string // UUID of the folder (but not full ID)
|
||||
FolderId string // UUID of the folder (but not full ID/UID)
|
||||
FolderName string // Folder path if the item is nested under one or more folders
|
||||
RequestID string // UUID of the request (but not full ID)
|
||||
RequestId string // UUID of the folder (but not full ID/UID)
|
||||
RequestName string
|
||||
FullID string //full ID of the reference item (created_by + ID) OR just the UUID
|
||||
Id string // Postman designated object ID (not the UID)
|
||||
Link string //direct link to the folder (could be .json file path)
|
||||
Type string //folder, request, etc.
|
||||
EnvironmentName string
|
||||
@@ -94,12 +93,11 @@ type Collection struct {
|
||||
}
|
||||
|
||||
type Info struct {
|
||||
PostmanID string `json:"_postman_id"` // This is a UUID. Needs createdBy ID prefix to be used with API.
|
||||
Id string `json:"id"` // This is a UUID, but not a UID
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Schema string `json:"schema"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Uid string `json:"uid"` //Need to use this to get the collection via API
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
@@ -112,7 +110,6 @@ type Item struct {
|
||||
Request Request `json:"request,omitempty"`
|
||||
Response []Response `json:"response,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Uid string `json:"uid,omitempty"` //Need to use this to get the collection via API. The UID is a concatenation of the ID and the user ID of whoever created the item.
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
@@ -180,7 +177,6 @@ type Response struct {
|
||||
HeaderKeyValue []KeyValue
|
||||
HeaderString []string
|
||||
Body string `json:"body,omitempty"`
|
||||
Uid string `json:"uid,omitempty"`
|
||||
}
|
||||
|
||||
// A Client manages communication with the Postman API.
|
||||
@@ -308,64 +304,64 @@ func (c *Client) EnumerateWorkspaces(ctx context.Context) ([]Workspace, error) {
|
||||
}
|
||||
|
||||
// GetWorkspace returns the workspace for a given workspace
|
||||
func (c *Client) GetWorkspace(ctx context.Context, workspaceUUID string) (Workspace, error) {
|
||||
ctx.Logger().V(2).Info("getting workspace", "workspace", workspaceUUID)
|
||||
func (c *Client) GetWorkspace(ctx context.Context, workspaceId string) (Workspace, error) {
|
||||
ctx.Logger().V(2).Info("getting workspace", "workspace", workspaceId)
|
||||
obj := struct {
|
||||
Workspace Workspace `json:"workspace"`
|
||||
}{}
|
||||
|
||||
url := fmt.Sprintf(WORKSPACE_URL, workspaceUUID)
|
||||
url := fmt.Sprintf(WORKSPACE_URL, workspaceId)
|
||||
if err := c.WorkspaceAndCollectionRateLimiter.Wait(ctx); err != nil {
|
||||
return Workspace{}, fmt.Errorf("could not wait for rate limiter during workspace getting: %w", err)
|
||||
}
|
||||
body, err := c.getPostmanResponseBodyBytes(ctx, url, nil)
|
||||
if err != nil {
|
||||
return Workspace{}, fmt.Errorf("could not get postman workspace (%s) response bytes: %w", workspaceUUID, err)
|
||||
return Workspace{}, fmt.Errorf("could not get postman workspace (%s) response bytes: %w", workspaceId, err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(body), &obj); err != nil {
|
||||
return Workspace{}, fmt.Errorf("could not unmarshal workspace JSON for workspace (%s): %w", workspaceUUID, err)
|
||||
return Workspace{}, fmt.Errorf("could not unmarshal workspace JSON for workspace (%s): %w", workspaceId, err)
|
||||
}
|
||||
|
||||
return obj.Workspace, nil
|
||||
}
|
||||
|
||||
// GetEnvironmentVariables returns the environment variables for a given environment
|
||||
func (c *Client) GetEnvironmentVariables(ctx context.Context, environment_uuid string) (VariableData, error) {
|
||||
func (c *Client) GetEnvironmentVariables(ctx context.Context, environmentId string) (VariableData, error) {
|
||||
obj := struct {
|
||||
VariableData VariableData `json:"environment"`
|
||||
}{}
|
||||
|
||||
url := fmt.Sprintf(ENVIRONMENTS_URL, environment_uuid)
|
||||
url := fmt.Sprintf(ENVIRONMENTS_URL, environmentId)
|
||||
if err := c.GeneralRateLimiter.Wait(ctx); err != nil {
|
||||
return VariableData{}, fmt.Errorf("could not wait for rate limiter during environment variable getting: %w", err)
|
||||
}
|
||||
body, err := c.getPostmanResponseBodyBytes(ctx, url, nil)
|
||||
if err != nil {
|
||||
return VariableData{}, fmt.Errorf("could not get postman environment (%s) response bytes: %w", environment_uuid, err)
|
||||
return VariableData{}, fmt.Errorf("could not get postman environment (%s) response bytes: %w", environmentId, err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(body), &obj); err != nil {
|
||||
return VariableData{}, fmt.Errorf("could not unmarshal env variables JSON for environment (%s): %w", environment_uuid, err)
|
||||
return VariableData{}, fmt.Errorf("could not unmarshal env variables JSON for environment (%s): %w", environmentId, err)
|
||||
}
|
||||
|
||||
return obj.VariableData, nil
|
||||
}
|
||||
|
||||
// GetCollection returns the collection for a given collection
|
||||
func (c *Client) GetCollection(ctx context.Context, collection_uuid string) (Collection, error) {
|
||||
func (c *Client) GetCollection(ctx context.Context, collectionId string) (Collection, error) {
|
||||
obj := struct {
|
||||
Collection Collection `json:"collection"`
|
||||
}{}
|
||||
|
||||
url := fmt.Sprintf(COLLECTIONS_URL, collection_uuid)
|
||||
url := fmt.Sprintf(COLLECTIONS_URL, collectionId)
|
||||
if err := c.WorkspaceAndCollectionRateLimiter.Wait(ctx); err != nil {
|
||||
return Collection{}, fmt.Errorf("could not wait for rate limiter during collection getting: %w", err)
|
||||
}
|
||||
body, err := c.getPostmanResponseBodyBytes(ctx, url, nil)
|
||||
if err != nil {
|
||||
return Collection{}, fmt.Errorf("could not get postman collection (%s) response bytes: %w", collection_uuid, err)
|
||||
return Collection{}, fmt.Errorf("could not get postman collection (%s) response bytes: %w", collectionId, err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(body), &obj); err != nil {
|
||||
return Collection{}, fmt.Errorf("could not unmarshal JSON for collection (%s): %w", collection_uuid, err)
|
||||
return Collection{}, fmt.Errorf("could not unmarshal JSON for collection (%s): %w", collectionId, err)
|
||||
}
|
||||
|
||||
// Loop used to deal with seeing whether a request/response header is a string or a key value pair
|
||||
@@ -374,7 +370,7 @@ func (c *Client) GetCollection(ctx context.Context, collection_uuid string) (Col
|
||||
if err := json.Unmarshal(obj.Collection.Items[i].Request.HeaderRaw, &obj.Collection.Items[i].Request.HeaderKeyValue); err == nil {
|
||||
} else if err := json.Unmarshal(obj.Collection.Items[i].Request.HeaderRaw, &obj.Collection.Items[i].Request.HeaderString); err == nil {
|
||||
} else {
|
||||
return Collection{}, fmt.Errorf("could not unmarshal request header JSON for collection (%s): %w", collection_uuid, err)
|
||||
return Collection{}, fmt.Errorf("could not unmarshal request header JSON for collection (%s): %w", collectionId, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,13 +378,13 @@ func (c *Client) GetCollection(ctx context.Context, collection_uuid string) (Col
|
||||
if err := json.Unmarshal(obj.Collection.Items[i].Response[j].OriginalRequest.HeaderRaw, &obj.Collection.Items[i].Response[j].OriginalRequest.HeaderKeyValue); err == nil {
|
||||
} else if err := json.Unmarshal(obj.Collection.Items[i].Response[j].OriginalRequest.HeaderRaw, &obj.Collection.Items[i].Response[j].OriginalRequest.HeaderString); err == nil {
|
||||
} else {
|
||||
return Collection{}, fmt.Errorf("could not unmarshal original request header in response JSON for collection (%s): %w", collection_uuid, err)
|
||||
return Collection{}, fmt.Errorf("could not unmarshal original request header in response JSON for collection (%s): %w", collectionId, err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(obj.Collection.Items[i].Response[j].HeaderRaw, &obj.Collection.Items[i].Response[j].HeaderKeyValue); err == nil {
|
||||
} else if err := json.Unmarshal(obj.Collection.Items[i].Response[j].HeaderRaw, &obj.Collection.Items[i].Response[j].HeaderString); err == nil {
|
||||
} else {
|
||||
return Collection{}, fmt.Errorf("could not unmarshal response header JSON for collection (%s): %w", collection_uuid, err)
|
||||
return Collection{}, fmt.Errorf("could not unmarshal response header JSON for collection (%s): %w", collectionId, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@ package postman
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -12,6 +15,7 @@ import (
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||
"gopkg.in/h2non/gock.v1"
|
||||
|
||||
"github.com/repeale/fp-go"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
@@ -71,8 +75,8 @@ func TestSource_ScanCollection(t *testing.T) {
|
||||
name: "GET request with URL",
|
||||
collection: Collection{
|
||||
Info: Info{
|
||||
PostmanID: "col1",
|
||||
Name: "Test Collection",
|
||||
Id: "col1",
|
||||
Name: "Test Collection",
|
||||
},
|
||||
Items: []Item{
|
||||
{
|
||||
@@ -97,8 +101,8 @@ func TestSource_ScanCollection(t *testing.T) {
|
||||
name: "POST request with URL and auth",
|
||||
collection: Collection{
|
||||
Info: Info{
|
||||
PostmanID: "col2",
|
||||
Name: "Test Collection",
|
||||
Id: "col2",
|
||||
Name: "Test Collection",
|
||||
},
|
||||
Items: []Item{
|
||||
{
|
||||
@@ -180,8 +184,8 @@ func TestSource_ScanVariableData(t *testing.T) {
|
||||
name: "Single variable",
|
||||
metadata: Metadata{
|
||||
CollectionInfo: Info{
|
||||
PostmanID: "col1",
|
||||
Name: "Test Collection",
|
||||
Id: "col1",
|
||||
Name: "Test Collection",
|
||||
},
|
||||
},
|
||||
variableData: VariableData{
|
||||
@@ -200,8 +204,8 @@ func TestSource_ScanVariableData(t *testing.T) {
|
||||
name: "Multiple variables",
|
||||
metadata: Metadata{
|
||||
CollectionInfo: Info{
|
||||
PostmanID: "col2",
|
||||
Name: "Test Collection",
|
||||
Id: "col2",
|
||||
Name: "Test Collection",
|
||||
},
|
||||
},
|
||||
variableData: VariableData{
|
||||
@@ -686,3 +690,141 @@ func TestSource_HeadersScanning(t *testing.T) {
|
||||
t.Logf("Generated %d chunks from the mock data", chunksReceived)
|
||||
}
|
||||
}
|
||||
|
||||
func makePostmanUid[A, B any](userId A, objectId B) string {
|
||||
return fmt.Sprint(userId, '-', objectId)
|
||||
}
|
||||
|
||||
func Test_WorkspaceListUnmarshalling(t *testing.T) {
|
||||
// This test is designed to cover the unmarshalling of the response to a request
|
||||
// like: curl --location 'https://api.getpostman.com/workspaces'
|
||||
defer gock.Off()
|
||||
|
||||
aUserId := strconv.Itoa(10000000 + rand.Intn(99999999-10000000))
|
||||
|
||||
myWorkspaceId := uuid.New().String()
|
||||
myWorkspaceTestCollectionId := uuid.New().String()
|
||||
myWorkspaceTestCollectionUid := makePostmanUid(aUserId, myWorkspaceTestCollectionId)
|
||||
myWorkspaceTestEnvironemntId := uuid.New().String()
|
||||
myWorkspaceTestEnvironemntUid := makePostmanUid(aUserId, myWorkspaceTestEnvironemntId)
|
||||
|
||||
privateWorkspaceId := uuid.New().String()
|
||||
teamWorkspaceId := uuid.New().String()
|
||||
publicWorkspaceId := uuid.New().String()
|
||||
partnerWorkspaceId := uuid.New().String()
|
||||
|
||||
// Set up some responses
|
||||
|
||||
successfulWorkspaceListResponseStr := `
|
||||
{
|
||||
"workspaces": [
|
||||
{
|
||||
"id": "` + myWorkspaceId + `",
|
||||
"name": "My Workspace",
|
||||
"createdBy": "` + aUserId + `",
|
||||
"type": "personal",
|
||||
"visibility": "personal"
|
||||
},
|
||||
{
|
||||
"id": "` + privateWorkspaceId + `",
|
||||
"name": "Private Workspace",
|
||||
"createdBy": "` + aUserId + `",
|
||||
"type": "team",
|
||||
"visibility": "private"
|
||||
},
|
||||
{
|
||||
"id": "` + teamWorkspaceId + `",
|
||||
"name": "Team Workspace",
|
||||
"createdBy": "` + aUserId + `",
|
||||
"type": "team",
|
||||
"visibility": "team"
|
||||
},
|
||||
{
|
||||
"id": "` + publicWorkspaceId + `",
|
||||
"name": "Public Workspace",
|
||||
"createdBy": "` + aUserId + `",
|
||||
"type": "team",
|
||||
"visibility": "public"
|
||||
},
|
||||
{
|
||||
"id": "` + partnerWorkspaceId + `",
|
||||
"name": "Partner Workspace",
|
||||
"createdBy": "` + aUserId + `",
|
||||
"type": "team",
|
||||
"visibility": "partner"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
gock.New("https://api.getpostman.com").
|
||||
Get("/workspaces").
|
||||
Reply(200).
|
||||
BodyString(successfulWorkspaceListResponseStr)
|
||||
|
||||
successfulMyWorkspaceDetailsResponseStr := `
|
||||
{
|
||||
"workspace": {
|
||||
"id": "` + myWorkspaceId + `",
|
||||
"name": "Partner Workspace",
|
||||
"type": "team",
|
||||
"description": "This is a partner workspace.",
|
||||
"visibility": "partner",
|
||||
"createdBy": "` + aUserId + `",
|
||||
"updatedBy": "` + aUserId + `",
|
||||
"createdAt": "2022-07-06T16:18:32.000Z",
|
||||
"updatedAt": "2022-07-06T20:55:13.000Z",
|
||||
"collections": [
|
||||
{
|
||||
"id": "` + myWorkspaceTestCollectionId + `",
|
||||
"name": "Test Collection",
|
||||
"uid": "` + myWorkspaceTestCollectionUid + `"
|
||||
}
|
||||
],
|
||||
"environments": [
|
||||
{
|
||||
"id": "` + myWorkspaceTestEnvironemntId + `",
|
||||
"name": "Test Environment",
|
||||
"uid": "` + myWorkspaceTestEnvironemntUid + `"
|
||||
}
|
||||
],
|
||||
"mocks": [],
|
||||
"monitors": [],
|
||||
"apis": [],
|
||||
"scim": {
|
||||
"createdBy": "405775fe15ed41872a8eea4c8aa2b38cda9749812cc55c99",
|
||||
"updatedBy": "405775fe15ed41872a8eea4c8aa2b38cda9749812cc55c99"
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
gock.New("https://api.getpostman.com").
|
||||
Get("/workspaces/" + myWorkspaceId).
|
||||
Reply(200).
|
||||
BodyString(successfulMyWorkspaceDetailsResponseStr)
|
||||
|
||||
// Generice setup that we have to do every time
|
||||
ctx := context.Background()
|
||||
s, conn := createTestSource(&sourcespb.Postman{
|
||||
Credential: &sourcespb.Postman_Token{
|
||||
Token: "super-secret-token",
|
||||
},
|
||||
})
|
||||
s.Init(ctx, "test - postman", 0, 1, false, conn, 1)
|
||||
gock.InterceptClient(s.client.HTTPClient)
|
||||
defer gock.RestoreClient(s.client.HTTPClient)
|
||||
|
||||
// Kick off the thing. At this point we expect calls to the following:
|
||||
// 1) https://api.getpostman.com/workspaces
|
||||
// - to get the initial list of workspaces
|
||||
// 2) https://api.getpostman.com/workspaces/{workspace_id}
|
||||
// - for _each_ of the returned workspaces, to get their full information
|
||||
workspaces, _ := s.client.EnumerateWorkspaces(ctx)
|
||||
|
||||
// First we check that the returned list of workspaces looks exactly like we want it to
|
||||
actualWorkspaceIds := fp.Map(func(w Workspace) string { return w.Id })(workspaces)
|
||||
expectedWorkspaceIds := []string{myWorkspaceId, privateWorkspaceId, publicWorkspaceId, partnerWorkspaceId, teamWorkspaceId}
|
||||
assert.ElementsMatch(t, actualWorkspaceIds, expectedWorkspaceIds)
|
||||
|
||||
// Now we check that the appropriate calls were made
|
||||
fmt.Printf("%v", workspaces)
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func (s *Source) buildSubstitution(data string, metadata Metadata, combos *map[s
|
||||
matches := removeDuplicateStr(subRe.FindAllString(data, -1))
|
||||
for _, match := range matches {
|
||||
for _, slice := range s.sub.variables[strings.Trim(match, "{}")] {
|
||||
if slice.Metadata.CollectionInfo.PostmanID != "" && slice.Metadata.CollectionInfo.PostmanID != metadata.CollectionInfo.PostmanID {
|
||||
if slice.Metadata.CollectionInfo.Id != "" && slice.Metadata.CollectionInfo.Id != metadata.CollectionInfo.Id {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestNewSubstitution(t *testing.T) {
|
||||
func TestSubstitution_Add(t *testing.T) {
|
||||
sub := NewSubstitution()
|
||||
metadata := Metadata{
|
||||
CollectionInfo: Info{PostmanID: "col1"},
|
||||
CollectionInfo: Info{Id: "col1"},
|
||||
}
|
||||
sub.add(metadata, "key1", "value1")
|
||||
sub.add(metadata, "key1", "value2")
|
||||
|
||||
Reference in New Issue
Block a user