This commit is contained in:
Martin Locklear
2025-05-08 14:33:32 -04:00
parent 5f56550804
commit 462ac60b33
6 changed files with 238 additions and 98 deletions
+1
View File
@@ -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
+50 -49
View File
@@ -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
+35 -39
View File
@@ -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)
}
}
}
+150 -8
View File
@@ -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)
}
+1 -1
View File
@@ -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
}
+1 -1
View File
@@ -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")