WIP
This commit is contained in:
@@ -83,6 +83,7 @@ require (
|
|||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.20.5
|
github.com/prometheus/client_golang v1.20.5
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0
|
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/sassoftware/go-rpmutils v0.4.0
|
||||||
github.com/schollz/progressbar/v3 v3.17.1
|
github.com/schollz/progressbar/v3 v3.17.1
|
||||||
github.com/sendgrid/sendgrid-go v3.16.0+incompatible
|
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 {
|
if err = json.Unmarshal(contents, &env); err != nil {
|
||||||
return err
|
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
|
// Scan local collections
|
||||||
@@ -235,8 +235,8 @@ func (s *Source) scanLocalWorkspace(ctx context.Context, chunksChan chan *source
|
|||||||
s.resetKeywords()
|
s.resetKeywords()
|
||||||
|
|
||||||
metadata := Metadata{
|
metadata := Metadata{
|
||||||
WorkspaceUUID: workspace.Id,
|
WorkspaceId: workspace.Id,
|
||||||
fromLocal: true,
|
fromLocal: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, environment := range workspace.EnvironmentsRaw {
|
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
|
metadata.LocationType = source_metadatapb.PostmanLocationType_UNKNOWN_POSTMAN
|
||||||
}
|
}
|
||||||
for _, collection := range workspace.CollectionsRaw {
|
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)
|
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
|
// initiate metadata to track the tree structure of postman data
|
||||||
metadata := Metadata{
|
metadata := Metadata{
|
||||||
WorkspaceUUID: workspace.Id,
|
WorkspaceId: workspace.Id,
|
||||||
WorkspaceName: workspace.Name,
|
WorkspaceName: workspace.Name,
|
||||||
CreatedBy: workspace.CreatedBy,
|
CreatedBy: workspace.CreatedBy,
|
||||||
Type: "workspace",
|
Type: "workspace",
|
||||||
@@ -266,21 +266,21 @@ func (s *Source) scanWorkspace(ctx context.Context, chunksChan chan *sources.Chu
|
|||||||
|
|
||||||
// gather and scan environment variables
|
// gather and scan environment variables
|
||||||
for _, envID := range workspace.Environments {
|
for _, envID := range workspace.Environments {
|
||||||
envVars, err := s.client.GetEnvironmentVariables(ctx, envID.Uid)
|
envVars, err := s.client.GetEnvironmentVariables(ctx, envID.Id)
|
||||||
if err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
if shouldSkip(envID.Uid, s.conn.IncludeEnvironments, s.conn.ExcludeEnvironments) {
|
if shouldSkip(envID.Id, s.conn.IncludeEnvironments, s.conn.ExcludeEnvironments) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
metadata.Type = ENVIRONMENT_TYPE
|
metadata.Type = ENVIRONMENT_TYPE
|
||||||
metadata.Link = LINK_BASE_URL + "environments/" + envID.Uid
|
metadata.Link = LINK_BASE_URL + "environments/" + envID.Id
|
||||||
metadata.FullID = envVars.Id
|
metadata.Id = envVars.Id
|
||||||
metadata.EnvironmentID = envID.Uid
|
metadata.EnvironmentId = envID.Id
|
||||||
metadata.EnvironmentName = envVars.Name
|
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, " ") {
|
for _, word := range strings.Split(envVars.Name, " ") {
|
||||||
s.attemptToAddKeyword(word)
|
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.LocationType = source_metadatapb.PostmanLocationType_UNKNOWN_POSTMAN
|
||||||
metadata.Type = ""
|
metadata.Type = ""
|
||||||
metadata.Link = ""
|
metadata.Link = ""
|
||||||
metadata.FullID = ""
|
metadata.Id = ""
|
||||||
metadata.EnvironmentID = ""
|
metadata.EnvironmentId = ""
|
||||||
metadata.EnvironmentName = ""
|
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")
|
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
|
// at this point we have all the possible
|
||||||
// substitutions from Environment variables
|
// substitutions from Environment variables
|
||||||
for _, collectionID := range workspace.Collections {
|
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
|
continue
|
||||||
}
|
}
|
||||||
collection, err := s.client.GetCollection(ctx, collectionID.Uid)
|
collection, err := s.client.GetCollection(ctx, collectionID.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Log and move on, because sometimes the Postman API seems to give us collection IDs
|
// 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.
|
// 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.
|
// scanCollection scans a collection and all its items, folders, and requests.
|
||||||
// locally scoped Metadata is updated as we drill down into the collection.
|
// 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) {
|
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.CollectionInfo = collection.Info
|
||||||
metadata.Type = COLLECTION_TYPE
|
metadata.Type = COLLECTION_TYPE
|
||||||
s.attemptToAddKeyword(collection.Info.Name)
|
s.attemptToAddKeyword(collection.Info.Name)
|
||||||
|
|
||||||
if !metadata.fromLocal {
|
if !metadata.fromLocal {
|
||||||
metadata.FullID = metadata.CollectionInfo.Uid
|
metadata.Id = metadata.CollectionInfo.Id
|
||||||
metadata.Link = LINK_BASE_URL + COLLECTION_TYPE + "/" + metadata.FullID
|
metadata.Link = LINK_BASE_URL + COLLECTION_TYPE + "/" + metadata.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.LocationType = source_metadatapb.PostmanLocationType_COLLECTION_VARIABLE
|
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
|
metadata.FolderName = item.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.Uid != "" {
|
if item.Id != "" {
|
||||||
metadata.FullID = item.Uid
|
metadata.Id = item.Id
|
||||||
metadata.Link = LINK_BASE_URL + FOLDER_TYPE + "/" + metadata.FullID
|
metadata.Link = LINK_BASE_URL + FOLDER_TYPE + "/" + metadata.Id
|
||||||
}
|
}
|
||||||
// recurse through the folders
|
// recurse through the folders
|
||||||
for _, subItem := range item.Items {
|
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.
|
// The assignment of the folder ID to be the current item ID is due to wanting to assume that your current item
|
||||||
// If your current item is a folder, you will want the folder ID to match the UID of the current item.
|
// is a folder unless you have request data inside of your item.
|
||||||
// If your current item is a request, you will want the folder ID to match the UID of the parent folder.
|
// 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.
|
// 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
|
// check if there are any requests in the folder
|
||||||
if item.Request.Method != "" {
|
if item.Request.Method != "" {
|
||||||
metadata.FolderName = strings.Replace(metadata.FolderName, (" > " + item.Name), "", -1)
|
metadata.FolderName = strings.Replace(metadata.FolderName, (" > " + item.Name), "", -1)
|
||||||
metadata.FolderID = parentItemId
|
metadata.FolderId = parentItemId
|
||||||
if metadata.FolderID == "" {
|
if metadata.FolderId == "" {
|
||||||
metadata.FolderName = ""
|
metadata.FolderName = ""
|
||||||
}
|
}
|
||||||
metadata.RequestID = item.Uid
|
metadata.RequestId = item.Id
|
||||||
metadata.RequestName = item.Name
|
metadata.RequestName = item.Name
|
||||||
metadata.Type = REQUEST_TYPE
|
metadata.Type = REQUEST_TYPE
|
||||||
if item.Uid != "" {
|
if item.Id != "" {
|
||||||
// Route to API endpoint
|
// Route to API endpoint
|
||||||
metadata.FullID = item.Uid
|
metadata.Id = item.Id
|
||||||
metadata.Link = LINK_BASE_URL + REQUEST_TYPE + "/" + item.Uid
|
metadata.Link = LINK_BASE_URL + REQUEST_TYPE + "/" + item.Id
|
||||||
} else {
|
} else {
|
||||||
// Route to collection.json
|
// Route to collection.json
|
||||||
metadata.FullID = item.Id
|
metadata.Id = item.Id
|
||||||
}
|
}
|
||||||
s.scanHTTPRequest(ctx, chunksChan, metadata, item.Request)
|
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
|
// Prep direct links. Ignore updating link if it's a local JSON file
|
||||||
if !metadata.fromLocal {
|
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" {
|
if event.Listen == "prerequest" {
|
||||||
metadata.Link += "?tab=pre-request-scripts"
|
metadata.Link += "?tab=pre-request-scripts"
|
||||||
} else {
|
} 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) {
|
func (s *Source) scanHTTPResponse(ctx context.Context, chunksChan chan *sources.Chunk, m Metadata, response Response) {
|
||||||
if response.Uid != "" {
|
if response.Id != "" {
|
||||||
m.Link = LINK_BASE_URL + "example/" + response.Uid
|
m.Link = LINK_BASE_URL + "example/" + response.Id
|
||||||
m.FullID = response.Uid
|
m.Id = response.Id
|
||||||
}
|
}
|
||||||
originalType := m.Type
|
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) {
|
func (s *Source) scanVariableData(ctx context.Context, chunksChan chan *sources.Chunk, m Metadata, variableData VariableData) {
|
||||||
if len(variableData.KeyValues) == 0 {
|
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
|
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) {
|
func (s *Source) scanData(ctx context.Context, chunksChan chan *sources.Chunk, data string, metadata Metadata) {
|
||||||
if data == "" {
|
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
|
return
|
||||||
}
|
}
|
||||||
if metadata.FieldType == "" {
|
if metadata.FieldType == "" {
|
||||||
@@ -718,15 +719,15 @@ func (s *Source) scanData(ctx context.Context, chunksChan chan *sources.Chunk, d
|
|||||||
Data: &source_metadatapb.MetaData_Postman{
|
Data: &source_metadatapb.MetaData_Postman{
|
||||||
Postman: &source_metadatapb.Postman{
|
Postman: &source_metadatapb.Postman{
|
||||||
Link: metadata.Link,
|
Link: metadata.Link,
|
||||||
WorkspaceUuid: metadata.WorkspaceUUID,
|
WorkspaceUuid: metadata.WorkspaceId,
|
||||||
WorkspaceName: metadata.WorkspaceName,
|
WorkspaceName: metadata.WorkspaceName,
|
||||||
CollectionId: metadata.CollectionInfo.Uid,
|
CollectionId: metadata.CollectionInfo.Id,
|
||||||
CollectionName: metadata.CollectionInfo.Name,
|
CollectionName: metadata.CollectionInfo.Name,
|
||||||
EnvironmentId: metadata.EnvironmentID,
|
EnvironmentId: metadata.EnvironmentId,
|
||||||
EnvironmentName: metadata.EnvironmentName,
|
EnvironmentName: metadata.EnvironmentName,
|
||||||
RequestId: metadata.RequestID,
|
RequestId: metadata.RequestId,
|
||||||
RequestName: metadata.RequestName,
|
RequestName: metadata.RequestName,
|
||||||
FolderId: metadata.FolderID,
|
FolderId: metadata.FolderId,
|
||||||
FolderName: metadata.FolderName,
|
FolderName: metadata.FolderName,
|
||||||
FieldType: metadata.FieldType,
|
FieldType: metadata.FieldType,
|
||||||
LocationType: metadata.LocationType,
|
LocationType: metadata.LocationType,
|
||||||
@@ -776,11 +777,11 @@ func unpackWorkspace(workspacePath string) (Workspace, error) {
|
|||||||
return workspace, nil
|
return workspace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldSkip(uuid string, include []string, exclude []string) bool {
|
func shouldSkip(object_id string, include []string, exclude []string) bool {
|
||||||
if slices.Contains(exclude, uuid) {
|
if slices.Contains(exclude, object_id) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if len(include) > 0 && !slices.Contains(include, uuid) {
|
if len(include) > 0 && !slices.Contains(include, object_id) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -22,25 +22,24 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Workspace struct {
|
type Workspace struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Visibility string `json:"visibility"`
|
Visibility string `json:"visibility"`
|
||||||
CreatedBy string `json:"createdBy"`
|
CreatedBy string `json:"createdBy"`
|
||||||
UpdatedBy string `json:"updatedBy"`
|
UpdatedBy string `json:"updatedBy"`
|
||||||
CreatedAt string `json:"createdAt"`
|
CreatedAt string `json:"createdAt"`
|
||||||
UpdatedAt string `json:"updatedAt"`
|
UpdatedAt string `json:"updatedAt"`
|
||||||
Collections []IdNameUid `json:"collections"`
|
Collections []IdName `json:"collections"`
|
||||||
Environments []IdNameUid `json:"environments"`
|
Environments []IdName `json:"environments"`
|
||||||
CollectionsRaw []Collection
|
CollectionsRaw []Collection
|
||||||
EnvironmentsRaw []VariableData
|
EnvironmentsRaw []VariableData
|
||||||
}
|
}
|
||||||
|
|
||||||
type IdNameUid struct {
|
type IdName struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Uid string `json:"uid"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type KeyValue struct {
|
type KeyValue struct {
|
||||||
@@ -53,7 +52,7 @@ type KeyValue struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type VariableData 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"`
|
Name string `json:"name"`
|
||||||
KeyValues []KeyValue `json:"values"`
|
KeyValues []KeyValue `json:"values"`
|
||||||
Owner string `json:"owner"`
|
Owner string `json:"owner"`
|
||||||
@@ -67,16 +66,16 @@ type Environment struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
WorkspaceUUID string
|
WorkspaceId string
|
||||||
WorkspaceName string
|
WorkspaceName string
|
||||||
CreatedBy string
|
CreatedBy string
|
||||||
EnvironmentID string
|
EnvironmentId string
|
||||||
CollectionInfo Info
|
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
|
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
|
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)
|
Link string //direct link to the folder (could be .json file path)
|
||||||
Type string //folder, request, etc.
|
Type string //folder, request, etc.
|
||||||
EnvironmentName string
|
EnvironmentName string
|
||||||
@@ -94,12 +93,11 @@ type Collection struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Info 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"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Schema string `json:"schema"`
|
Schema string `json:"schema"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
Uid string `json:"uid"` //Need to use this to get the collection via API
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
@@ -112,7 +110,6 @@ type Item struct {
|
|||||||
Request Request `json:"request,omitempty"`
|
Request Request `json:"request,omitempty"`
|
||||||
Response []Response `json:"response,omitempty"`
|
Response []Response `json:"response,omitempty"`
|
||||||
Description string `json:"description,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 {
|
type Auth struct {
|
||||||
@@ -180,7 +177,6 @@ type Response struct {
|
|||||||
HeaderKeyValue []KeyValue
|
HeaderKeyValue []KeyValue
|
||||||
HeaderString []string
|
HeaderString []string
|
||||||
Body string `json:"body,omitempty"`
|
Body string `json:"body,omitempty"`
|
||||||
Uid string `json:"uid,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Client manages communication with the Postman API.
|
// 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
|
// GetWorkspace returns the workspace for a given workspace
|
||||||
func (c *Client) GetWorkspace(ctx context.Context, workspaceUUID string) (Workspace, error) {
|
func (c *Client) GetWorkspace(ctx context.Context, workspaceId string) (Workspace, error) {
|
||||||
ctx.Logger().V(2).Info("getting workspace", "workspace", workspaceUUID)
|
ctx.Logger().V(2).Info("getting workspace", "workspace", workspaceId)
|
||||||
obj := struct {
|
obj := struct {
|
||||||
Workspace Workspace `json:"workspace"`
|
Workspace Workspace `json:"workspace"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
url := fmt.Sprintf(WORKSPACE_URL, workspaceUUID)
|
url := fmt.Sprintf(WORKSPACE_URL, workspaceId)
|
||||||
if err := c.WorkspaceAndCollectionRateLimiter.Wait(ctx); err != nil {
|
if err := c.WorkspaceAndCollectionRateLimiter.Wait(ctx); err != nil {
|
||||||
return Workspace{}, fmt.Errorf("could not wait for rate limiter during workspace getting: %w", err)
|
return Workspace{}, fmt.Errorf("could not wait for rate limiter during workspace getting: %w", err)
|
||||||
}
|
}
|
||||||
body, err := c.getPostmanResponseBodyBytes(ctx, url, nil)
|
body, err := c.getPostmanResponseBodyBytes(ctx, url, nil)
|
||||||
if err != 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 {
|
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
|
return obj.Workspace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnvironmentVariables returns the environment variables for a given environment
|
// 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 {
|
obj := struct {
|
||||||
VariableData VariableData `json:"environment"`
|
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 {
|
if err := c.GeneralRateLimiter.Wait(ctx); err != nil {
|
||||||
return VariableData{}, fmt.Errorf("could not wait for rate limiter during environment variable getting: %w", err)
|
return VariableData{}, fmt.Errorf("could not wait for rate limiter during environment variable getting: %w", err)
|
||||||
}
|
}
|
||||||
body, err := c.getPostmanResponseBodyBytes(ctx, url, nil)
|
body, err := c.getPostmanResponseBodyBytes(ctx, url, nil)
|
||||||
if err != 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 {
|
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
|
return obj.VariableData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCollection returns the collection for a given collection
|
// 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 {
|
obj := struct {
|
||||||
Collection Collection `json:"collection"`
|
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 {
|
if err := c.WorkspaceAndCollectionRateLimiter.Wait(ctx); err != nil {
|
||||||
return Collection{}, fmt.Errorf("could not wait for rate limiter during collection getting: %w", err)
|
return Collection{}, fmt.Errorf("could not wait for rate limiter during collection getting: %w", err)
|
||||||
}
|
}
|
||||||
body, err := c.getPostmanResponseBodyBytes(ctx, url, nil)
|
body, err := c.getPostmanResponseBodyBytes(ctx, url, nil)
|
||||||
if err != 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 {
|
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
|
// 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 {
|
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 if err := json.Unmarshal(obj.Collection.Items[i].Request.HeaderRaw, &obj.Collection.Items[i].Request.HeaderString); err == nil {
|
||||||
} else {
|
} 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 {
|
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 if err := json.Unmarshal(obj.Collection.Items[i].Response[j].OriginalRequest.HeaderRaw, &obj.Collection.Items[i].Response[j].OriginalRequest.HeaderString); err == nil {
|
||||||
} else {
|
} 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 {
|
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 if err := json.Unmarshal(obj.Collection.Items[i].Response[j].HeaderRaw, &obj.Collection.Items[i].Response[j].HeaderString); err == nil {
|
||||||
} else {
|
} 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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -12,6 +15,7 @@ import (
|
|||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||||
"gopkg.in/h2non/gock.v1"
|
"gopkg.in/h2non/gock.v1"
|
||||||
|
|
||||||
|
"github.com/repeale/fp-go"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||||
"google.golang.org/protobuf/types/known/anypb"
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
@@ -71,8 +75,8 @@ func TestSource_ScanCollection(t *testing.T) {
|
|||||||
name: "GET request with URL",
|
name: "GET request with URL",
|
||||||
collection: Collection{
|
collection: Collection{
|
||||||
Info: Info{
|
Info: Info{
|
||||||
PostmanID: "col1",
|
Id: "col1",
|
||||||
Name: "Test Collection",
|
Name: "Test Collection",
|
||||||
},
|
},
|
||||||
Items: []Item{
|
Items: []Item{
|
||||||
{
|
{
|
||||||
@@ -97,8 +101,8 @@ func TestSource_ScanCollection(t *testing.T) {
|
|||||||
name: "POST request with URL and auth",
|
name: "POST request with URL and auth",
|
||||||
collection: Collection{
|
collection: Collection{
|
||||||
Info: Info{
|
Info: Info{
|
||||||
PostmanID: "col2",
|
Id: "col2",
|
||||||
Name: "Test Collection",
|
Name: "Test Collection",
|
||||||
},
|
},
|
||||||
Items: []Item{
|
Items: []Item{
|
||||||
{
|
{
|
||||||
@@ -180,8 +184,8 @@ func TestSource_ScanVariableData(t *testing.T) {
|
|||||||
name: "Single variable",
|
name: "Single variable",
|
||||||
metadata: Metadata{
|
metadata: Metadata{
|
||||||
CollectionInfo: Info{
|
CollectionInfo: Info{
|
||||||
PostmanID: "col1",
|
Id: "col1",
|
||||||
Name: "Test Collection",
|
Name: "Test Collection",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variableData: VariableData{
|
variableData: VariableData{
|
||||||
@@ -200,8 +204,8 @@ func TestSource_ScanVariableData(t *testing.T) {
|
|||||||
name: "Multiple variables",
|
name: "Multiple variables",
|
||||||
metadata: Metadata{
|
metadata: Metadata{
|
||||||
CollectionInfo: Info{
|
CollectionInfo: Info{
|
||||||
PostmanID: "col2",
|
Id: "col2",
|
||||||
Name: "Test Collection",
|
Name: "Test Collection",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variableData: VariableData{
|
variableData: VariableData{
|
||||||
@@ -686,3 +690,141 @@ func TestSource_HeadersScanning(t *testing.T) {
|
|||||||
t.Logf("Generated %d chunks from the mock data", chunksReceived)
|
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))
|
matches := removeDuplicateStr(subRe.FindAllString(data, -1))
|
||||||
for _, match := range matches {
|
for _, match := range matches {
|
||||||
for _, slice := range s.sub.variables[strings.Trim(match, "{}")] {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func TestNewSubstitution(t *testing.T) {
|
|||||||
func TestSubstitution_Add(t *testing.T) {
|
func TestSubstitution_Add(t *testing.T) {
|
||||||
sub := NewSubstitution()
|
sub := NewSubstitution()
|
||||||
metadata := Metadata{
|
metadata := Metadata{
|
||||||
CollectionInfo: Info{PostmanID: "col1"},
|
CollectionInfo: Info{Id: "col1"},
|
||||||
}
|
}
|
||||||
sub.add(metadata, "key1", "value1")
|
sub.add(metadata, "key1", "value1")
|
||||||
sub.add(metadata, "key1", "value2")
|
sub.add(metadata, "key1", "value2")
|
||||||
|
|||||||
Reference in New Issue
Block a user