mirror of
https://github.com/LukeHagar/api-specs.git
synced 2025-12-06 12:27:48 +00:00
added all checks
This commit is contained in:
2
postman-script/partialUpdate/.gitignore
vendored
2
postman-script/partialUpdate/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
.env
|
|
||||||
node_modules
|
|
||||||
@@ -1,507 +0,0 @@
|
|||||||
// Deploy collection inncrementally
|
|
||||||
// ---------------------------------------------------------------
|
|
||||||
// Cycle through the collection objects and deploy them one by one
|
|
||||||
// ---------------------------------------------------------------
|
|
||||||
// Folders -> Requests -> Responses
|
|
||||||
// ---------------------------------------------------------------
|
|
||||||
// An updated object has a different id.
|
|
||||||
// If the id matches, then the object is unchanged.
|
|
||||||
// If the id does not match, then the object is new.
|
|
||||||
// In the end delete all objects that are not in the updated collection.
|
|
||||||
// Sort the objects and update the parent order property.
|
|
||||||
// ---------------------------------------------------------------
|
|
||||||
// Except for folders which would force the update of the entire collection.
|
|
||||||
// ---------------------------------------------------------------
|
|
||||||
|
|
||||||
const pmConvert = require('./PostmanCovertions')
|
|
||||||
const pmAPI = require('./postmanAPI')
|
|
||||||
var crypto = require('crypto');
|
|
||||||
const { GenID } = require('./Utils')
|
|
||||||
|
|
||||||
const deployIncremental = async (privateRemoteCollectionId, localCollection, publicRemoteCollectionId) => {
|
|
||||||
let remoteCollection = await refreshRemoteCollection(privateRemoteCollectionId)
|
|
||||||
|
|
||||||
console.log('Incremental deployment of collection ', localCollection.info.name)
|
|
||||||
|
|
||||||
// const collectioHeadHasChanged =
|
|
||||||
await upadteCollectionHead(remoteCollection, localCollection)
|
|
||||||
|
|
||||||
remoteCollection = await refreshRemoteCollection(privateRemoteCollectionId)
|
|
||||||
|
|
||||||
// const foldersHaveChanged =
|
|
||||||
await mergeFolders(remoteCollection, localCollection)
|
|
||||||
|
|
||||||
remoteCollection = await refreshRemoteCollection(privateRemoteCollectionId)
|
|
||||||
|
|
||||||
// const requestsHaveChanged =
|
|
||||||
await mergeRequests(remoteCollection, localCollection)
|
|
||||||
|
|
||||||
remoteCollection = await refreshRemoteCollection(privateRemoteCollectionId)
|
|
||||||
|
|
||||||
// const responsesHaveChanged =
|
|
||||||
await mergeResponses(remoteCollection, localCollection)
|
|
||||||
|
|
||||||
// should we always merge into the public collection?
|
|
||||||
// There is teh case that if an error happens in the merge phase
|
|
||||||
// the private collection is fully updated
|
|
||||||
// and in the next run the public collection will NOT be updated
|
|
||||||
// because there are no changes in the private collection
|
|
||||||
|
|
||||||
// if (!(collectioHeadHasChanged || foldersHaveChanged || requestsHaveChanged || responsesHaveChanged)) {
|
|
||||||
// console.log('Incremental deployment of collection ', localCollection.info.name, ' completed\n\n')
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
const msg = 'Merging to public collection'
|
|
||||||
console.log('\n' + msg + '...')
|
|
||||||
await new pmAPI.Collection(privateRemoteCollectionId).merge(publicRemoteCollectionId)
|
|
||||||
.then(() => { console.log(msg, '-> OK\n') })
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
console.log('Incremental deployment of collection ', localCollection.info.name, ' completed\n\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function upadteCollectionHead (remoteCollection, localCollection) {
|
|
||||||
// the colelction head shoul dbe updated if there are changes in
|
|
||||||
// Authorization
|
|
||||||
// Pre-request Script
|
|
||||||
// Tests
|
|
||||||
// Variables
|
|
||||||
|
|
||||||
const localEmptyCollection = { ...localCollection }
|
|
||||||
localEmptyCollection.item = []
|
|
||||||
|
|
||||||
// Check changes in info
|
|
||||||
const hasChangesInfo = checkInfoChanges(remoteCollection, localCollection)
|
|
||||||
|
|
||||||
// Check if there are changes in the Authorization
|
|
||||||
const hasChangesAuth = checkObjectChanges(remoteCollection.collection.auth, localEmptyCollection.auth)
|
|
||||||
|
|
||||||
// Check if there are changes in the Scripts (pre-request and tests)
|
|
||||||
const hasChangesPreRequestScript = checkScriptChanges('prerequest', remoteCollection, localEmptyCollection)
|
|
||||||
|
|
||||||
const hasChangesTestScript = checkScriptChanges('test', remoteCollection, localEmptyCollection)
|
|
||||||
|
|
||||||
// Check if there are changes in the Variables
|
|
||||||
const hasChangesVariables = checkVariableChanges(remoteCollection, localEmptyCollection)
|
|
||||||
|
|
||||||
const hasFolderSortChanges = checkFolderSortChanges(remoteCollection, localCollection)
|
|
||||||
|
|
||||||
const hasChanges = (
|
|
||||||
hasFolderSortChanges ||
|
|
||||||
hasChangesInfo ||
|
|
||||||
hasChangesAuth ||
|
|
||||||
hasChangesPreRequestScript ||
|
|
||||||
hasChangesTestScript ||
|
|
||||||
hasChangesVariables
|
|
||||||
)
|
|
||||||
|
|
||||||
if (hasChanges) {
|
|
||||||
const msg = 'Updating collection head'
|
|
||||||
console.log('\n' + msg + '...')
|
|
||||||
await new pmAPI.Collection(remoteCollection.collection.info.uid)
|
|
||||||
.update({ collection: localEmptyCollection })
|
|
||||||
.then(() => { console.log(msg, '-> OK\n') })
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return hasChanges
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkFolderSortChanges = (remoteCollection, localCollection) => {
|
|
||||||
const remoteFolders = remoteCollection.collection.item
|
|
||||||
.map(folder => ({ id: folder.id }))
|
|
||||||
const localFolders = localCollection.item
|
|
||||||
.filter(folder => !folder.folder)
|
|
||||||
.map(folder => ({ id: folder.id }))
|
|
||||||
|
|
||||||
const remoteFoldersHash = GenID(JSON.stringify(remoteFolders))
|
|
||||||
const localFoldersHash = GenID(JSON.stringify(localFolders))
|
|
||||||
|
|
||||||
return remoteFoldersHash !== localFoldersHash
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkInfoChanges = (remoteCollection, localEmptyCollection) => {
|
|
||||||
// collection info does not have a specific id
|
|
||||||
// so we need to generate a hash and compare them
|
|
||||||
// The hash is only beig generated for name, description and schema
|
|
||||||
|
|
||||||
const { name, description, schema } = remoteCollection.collection.info
|
|
||||||
const remoteInfo = { name, description, schema }
|
|
||||||
|
|
||||||
const { name: localName, description: localDescription, schema: localSchema } = localEmptyCollection.info
|
|
||||||
const localInfo = { name: localName, description: localDescription, schema: localSchema }
|
|
||||||
|
|
||||||
const remoteInfoHash = calculateHash(JSON.stringify(remoteInfo))
|
|
||||||
const localInfoHash = calculateHash(JSON.stringify(localInfo))
|
|
||||||
|
|
||||||
return remoteInfoHash !== localInfoHash
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkObjectChanges = (remoteCollectionObject, localCollectionObject) => {
|
|
||||||
if (!remoteCollectionObject && !localCollectionObject) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (!remoteCollectionObject || !localCollectionObject) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// certain object like auth do not have an id,
|
|
||||||
// so we need to generate on and compare them
|
|
||||||
const remoteCollectionAuthID = GenID(JSON.stringify(remoteCollectionObject))
|
|
||||||
const localCollectionAuthID = GenID(JSON.stringify(localCollectionObject))
|
|
||||||
return remoteCollectionAuthID !== localCollectionAuthID
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkScriptChanges = (scriptType, remoteCollection, localCollection) => {
|
|
||||||
// RB 2020-10-20: The collection may be empty or have no events at all
|
|
||||||
let remoteScript = null
|
|
||||||
let localScript = null
|
|
||||||
if (remoteCollection.collection.event) {
|
|
||||||
remoteScript = remoteCollection.collection.event.find(event => event.listen === scriptType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localCollection.event) {
|
|
||||||
localScript = localCollection.event.find(event => event.listen === scriptType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// const remoteScript = remoteCollection.collection.event.find(event => event.listen === scriptType)
|
|
||||||
// const localScript = localCollection.event.find(event => event.listen === scriptType)
|
|
||||||
|
|
||||||
if (!remoteScript && !localScript) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (!remoteScript || !localScript) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// files can be big, so we hash them
|
|
||||||
const remoteHash = calculateHash(remoteScript.script.exec[0])
|
|
||||||
const localHash = calculateHash(localScript.script.exec[0])
|
|
||||||
|
|
||||||
return remoteHash !== localHash
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkVariableChanges = (remoteCollection, localCollection) => {
|
|
||||||
const remoteVariables = remoteCollection.collection.variable
|
|
||||||
const localVariables = localCollection.variable.map(variable => ({ key: variable.key, value: variable.value }))
|
|
||||||
|
|
||||||
// check if null
|
|
||||||
if (!remoteVariables && !localVariables) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (!remoteVariables || !localVariables) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// although the local collection does have a deterministic id
|
|
||||||
// the remote variable looses that value when it is updated
|
|
||||||
// so we need to generate an id for the remote variable
|
|
||||||
|
|
||||||
const remoteVariablesHash = GenID(remoteVariables)
|
|
||||||
const localVariablesHash = GenID(localVariables)
|
|
||||||
|
|
||||||
return remoteVariablesHash !== localVariablesHash
|
|
||||||
}
|
|
||||||
|
|
||||||
async function mergeFolders (remoteCollection, localCollection) {
|
|
||||||
console.log(' Deploying Folders:')
|
|
||||||
|
|
||||||
const remoteFolders = getAllFoldersFromCollectionItem(remoteCollection.collection.item)
|
|
||||||
const localFolders = localCollection.item // all folders
|
|
||||||
|
|
||||||
const newFolders = localFolders.filter(localFolder => !remoteFolders.find(remoteFolder => remoteFolder.id === localFolder.id))
|
|
||||||
const oldFolders = remoteFolders.filter(remoteFolder => !localFolders.find(localFolder => localFolder.id === remoteFolder.id))
|
|
||||||
|
|
||||||
let hasChanges = newFolders.length > 0 || oldFolders.length > 0
|
|
||||||
|
|
||||||
if (!hasChanges) {
|
|
||||||
console.log(' -> No changes')
|
|
||||||
return hasChanges
|
|
||||||
}
|
|
||||||
|
|
||||||
// create new folders
|
|
||||||
for (const folder of newFolders) {
|
|
||||||
const msg = ` Creating new folder [${folder.name}]`
|
|
||||||
await new pmAPI.Folder(remoteCollection.collection.info.uid)
|
|
||||||
.create(folder)
|
|
||||||
.then(() => {
|
|
||||||
hasChanges = true
|
|
||||||
console.log(msg, '-> OK')
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete old folders
|
|
||||||
for (const folder of oldFolders) {
|
|
||||||
const msg = ` Deleting old folder [${folder.name}]`
|
|
||||||
await new pmAPI.Folder(remoteCollection.collection.info.uid)
|
|
||||||
.delete(folder.id)
|
|
||||||
.then(() => {
|
|
||||||
hasChanges = true
|
|
||||||
console.log(msg, '-> OK')
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort folders is not supported for now
|
|
||||||
// const order = localFolders.map(folder => folder.id)
|
|
||||||
// const msg = ' Sorting folders'
|
|
||||||
|
|
||||||
// // create a temporsary root folder
|
|
||||||
// const rootFolder = await new pmAPI.Folder(remoteCollection.collection.info.uid)
|
|
||||||
// .create({ id: GenID(), name: 'root', folders: order })
|
|
||||||
// .catch((error) => {
|
|
||||||
// console.log(msg, '-> FAIL')
|
|
||||||
// handlePostmanAPIError(error)
|
|
||||||
// })
|
|
||||||
// console.log('root folder', rootFolder)
|
|
||||||
// // move all remote folders into root folder
|
|
||||||
|
|
||||||
return hasChanges
|
|
||||||
}
|
|
||||||
|
|
||||||
async function mergeRequests (remoteCollection, localCollection) {
|
|
||||||
const remoteFolders = getAllFoldersFromCollectionItem(remoteCollection.collection.item)
|
|
||||||
const localFolders = localCollection.item // all folders
|
|
||||||
|
|
||||||
console.log('\n Deploying Requests:')
|
|
||||||
let anyRequestHasChanged = false
|
|
||||||
|
|
||||||
// loop folders
|
|
||||||
for (const localFolder of localFolders) {
|
|
||||||
const remoteRemoteFolder = remoteFolders.find(remoteFolder => ((remoteFolder.id === localFolder.id)))
|
|
||||||
|
|
||||||
// TODO: RB: get requests by folder
|
|
||||||
// handle undefined items
|
|
||||||
remoteRemoteFolder.item = remoteRemoteFolder.item || []
|
|
||||||
|
|
||||||
// filter out anything that is not a request
|
|
||||||
remoteRemoteFolder.item = remoteRemoteFolder.item.filter(request => request.request)
|
|
||||||
|
|
||||||
const remoteRequests = remoteRemoteFolder.item
|
|
||||||
const localRequests = localFolder.item
|
|
||||||
|
|
||||||
// Identify old and new requests
|
|
||||||
const newRequests = localRequests.filter(localRequest => !remoteRequests.find(remoteRequest => remoteRequest.id === localRequest.id))
|
|
||||||
const oldRequests = remoteRequests.filter(remoteRequest => !localRequests.find(localRequest => localRequest.id === remoteRequest.id))
|
|
||||||
|
|
||||||
const requestsInFolderHaveChanges = newRequests.length > 0 || oldRequests.length > 0
|
|
||||||
|
|
||||||
if (!requestsInFolderHaveChanges) {
|
|
||||||
console.log(' In Folder: ', localFolder.name, '-> No changes')
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
console.log(' In Folder: ', localFolder.name)
|
|
||||||
|
|
||||||
// create new requests
|
|
||||||
for (const request of newRequests) {
|
|
||||||
// check request format and convert if necessary
|
|
||||||
let pmRequest = null
|
|
||||||
if (!request.request) { // => Postman Format
|
|
||||||
pmRequest = request
|
|
||||||
} else { // => OpenAPI Format
|
|
||||||
pmRequest = pmConvert.requestFromLocal(request)
|
|
||||||
}
|
|
||||||
const msg = ` Creating new request [${request.name}]`
|
|
||||||
|
|
||||||
await new pmAPI.Request(remoteCollection.collection.info.uid)
|
|
||||||
.create(pmRequest, localFolder.id)
|
|
||||||
.then((req) => {
|
|
||||||
console.log(msg, '-> OK')
|
|
||||||
return req
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
// console.log('\nequest', request)
|
|
||||||
// console.log('\npmRequest', pmRequest)
|
|
||||||
// console.log('\nremoteRequest', remoteRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete old requests
|
|
||||||
for (const request of oldRequests) {
|
|
||||||
const msg = ` Deleting old request [${request.name}]`
|
|
||||||
await new pmAPI.Request(remoteCollection.collection.info.uid)
|
|
||||||
.delete(request.id)
|
|
||||||
.then(() => {
|
|
||||||
console.log(msg, '-> OK')
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestsInFolderHaveChanges) {
|
|
||||||
// sort requests in folder
|
|
||||||
const order = localRequests.map(request => request.id)
|
|
||||||
const msg = ` Sorting requests in folder [${localFolder.name}]`
|
|
||||||
await new pmAPI.Folder(remoteCollection.collection.info.uid)
|
|
||||||
.update(localFolder.id, { order })
|
|
||||||
.then(() => { console.log(msg, '-> OK') })
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
anyRequestHasChanged = anyRequestHasChanged || requestsInFolderHaveChanges
|
|
||||||
}
|
|
||||||
return anyRequestHasChanged
|
|
||||||
}
|
|
||||||
|
|
||||||
async function mergeResponses (remoteCollection, localCollection) {
|
|
||||||
console.log('\n Deploying Response:')
|
|
||||||
const remoteFolders = getAllFoldersFromCollectionItem(remoteCollection.collection.item)
|
|
||||||
const localFolders = localCollection.item
|
|
||||||
|
|
||||||
let anyResponseHasChanged = false
|
|
||||||
// loop folders
|
|
||||||
for (const localFolder of localFolders) {
|
|
||||||
const remoteRemoteFolder = remoteFolders.find(remoteFolder => ((remoteFolder.id === localFolder.id)))
|
|
||||||
|
|
||||||
// handle undefined items
|
|
||||||
remoteRemoteFolder.item = remoteRemoteFolder.item || []
|
|
||||||
|
|
||||||
// filter out anything that is not a request
|
|
||||||
remoteRemoteFolder.item = remoteRemoteFolder.item.filter(request => request.request)
|
|
||||||
|
|
||||||
const remoteRequests = remoteRemoteFolder.item
|
|
||||||
const localRequests = localFolder.item
|
|
||||||
|
|
||||||
console.log(' In Folder: ', localFolder.name)
|
|
||||||
// loop requests
|
|
||||||
for (const localRequest of localRequests) {
|
|
||||||
// Postman Request format does not have a response property
|
|
||||||
const remoteResponses = remoteRequests.find(remoteRequest => remoteRequest.id === localRequest.id).response
|
|
||||||
const localResponses = localRequest.response
|
|
||||||
// the request may not have responses
|
|
||||||
if (!localResponses) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const newResponses = localResponses.filter(localResponse => !remoteResponses.find(remoteResponse => remoteResponse.id === localResponse.id))
|
|
||||||
const oldResponses = remoteResponses.filter(remoteResponse => !localResponses.find(localResponse => localResponse.id === remoteResponse.id))
|
|
||||||
|
|
||||||
const ResponsesInReqquestHaveChanges = newResponses.length > 0 || oldResponses.length > 0
|
|
||||||
if (!ResponsesInReqquestHaveChanges) {
|
|
||||||
console.log(' In Request: ', localRequest.name, '-> No changes')
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
console.log(' In Request: ', localRequest.name)
|
|
||||||
|
|
||||||
// create new responses
|
|
||||||
for (const response of newResponses) {
|
|
||||||
const pmResponse = pmConvert.responseFromLocal(response)
|
|
||||||
const msg = ` Creating new response [${response.code} ${response.status}]`
|
|
||||||
await new pmAPI.Response(remoteCollection.collection.info.uid)
|
|
||||||
.create(pmResponse, localRequest.id)
|
|
||||||
.then(() => {
|
|
||||||
console.log(msg, '-> OK')
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete old responses
|
|
||||||
for (const response of oldResponses) {
|
|
||||||
const msg = ` Deleting old response [${response.code} ${response.status}]`
|
|
||||||
await new pmAPI.Response(remoteCollection.collection.info.uid)
|
|
||||||
.delete(response.id)
|
|
||||||
.then(() => {
|
|
||||||
console.log(msg, '-> OK')
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// updating the requests with the order of the responses, doesn't seem to be necessary
|
|
||||||
if (ResponsesInReqquestHaveChanges) {
|
|
||||||
// sort responses in requests
|
|
||||||
const responsesOrder = localResponses.map(response => response.id)
|
|
||||||
const msg = ` Sorting responses in request [${localRequest.name}]`
|
|
||||||
await new pmAPI.Request(remoteCollection.collection.info._postman_id)
|
|
||||||
.update(localRequest.id,
|
|
||||||
{
|
|
||||||
responses_order: responsesOrder
|
|
||||||
})
|
|
||||||
.then(() => { console.log(msg, '-> OK') })
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
anyResponseHasChanged = anyResponseHasChanged || ResponsesInReqquestHaveChanges
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return anyResponseHasChanged
|
|
||||||
}
|
|
||||||
|
|
||||||
async function refreshRemoteCollection (remoteCollectionID) {
|
|
||||||
const msg = 'Refreshing remote collection'
|
|
||||||
console.log('\n' + msg + '...\n')
|
|
||||||
const remoteCollection = await new pmAPI.Collection(remoteCollectionID).get()
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(msg, '-> FAIL')
|
|
||||||
handlePostmanAPIError(error)
|
|
||||||
})
|
|
||||||
return remoteCollection
|
|
||||||
}
|
|
||||||
|
|
||||||
// return all folders in the collection
|
|
||||||
// independently of where they are
|
|
||||||
const getAllFoldersFromCollectionItem = (collectionItem) => {
|
|
||||||
const folders = []
|
|
||||||
const processItem = (item) => {
|
|
||||||
if (!item.request && !item.responses) {
|
|
||||||
folders.push({ id: item.id, name: item.name, item: item.item })
|
|
||||||
}
|
|
||||||
if (item.item) {
|
|
||||||
item.item.forEach(processItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
collectionItem.forEach(processItem)
|
|
||||||
return folders
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle axios error
|
|
||||||
const handlePostmanAPIError = (error) => {
|
|
||||||
if (error.response) {
|
|
||||||
// The request was made and the server responded with a status code
|
|
||||||
// that falls out of the range of 2xx
|
|
||||||
console.log('API ERROR:', error.response.data)
|
|
||||||
} else {
|
|
||||||
// The request was made but no response was received
|
|
||||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
|
||||||
// http.ClientRequest in node.js
|
|
||||||
console.log('NO RESPONSE:', error.message)
|
|
||||||
if (error.cause) {
|
|
||||||
console.log('CAUSE:', error.cause)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const { method, url, data } = error.config
|
|
||||||
const smallData = data.substring(0, 1000)
|
|
||||||
console.log('REQUEST DETAILS', { method, url, smallData })
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateHash = (stringToHash) => {
|
|
||||||
return crypto.createHash('sha256').update(stringToHash).digest('hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
deployIncremental
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
// Postman Convertions
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
// Handles the conversions from the local generated object
|
|
||||||
// to the postman api specific objects.
|
|
||||||
// The converted objects are necessary for the updates to work.
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
// The interesting part is that the postman api uses a different
|
|
||||||
// object structure for folders, requests, adn responses,
|
|
||||||
// when compared to what you get from the collection json file.
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
|
|
||||||
const requestFromLocal = (localRequest) => {
|
|
||||||
// console.log('localRequest', localRequest)
|
|
||||||
let url = localRequest.request.url.protocol + '://' + localRequest.request.url.host + localRequest.request.url.path
|
|
||||||
|
|
||||||
let data = []
|
|
||||||
let dataMode = null
|
|
||||||
let rawModeData = null
|
|
||||||
if (localRequest.request.body && localRequest.request.body.urlencoded) {
|
|
||||||
data = dataFromLocalURLEncode(localRequest.request.body.urlencoded)
|
|
||||||
dataMode = localRequest.request.body.mode
|
|
||||||
rawModeData = localRequest.request.body.raw
|
|
||||||
}
|
|
||||||
|
|
||||||
let queryParams = []
|
|
||||||
if (localRequest.request.url.query) {
|
|
||||||
queryParams = dataFromLocalURLEncode(localRequest.request.url.query)
|
|
||||||
url += '?'
|
|
||||||
for (const param of queryParams) {
|
|
||||||
if (param.enabled === false) continue
|
|
||||||
url += param.key + '=' + param.value + '&'
|
|
||||||
}
|
|
||||||
url = url.slice(0, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
let pathVariableData = []
|
|
||||||
if (localRequest.request.url.variable) {
|
|
||||||
pathVariableData = dataFromLocalURLEncode(localRequest.request.url.variable)
|
|
||||||
}
|
|
||||||
|
|
||||||
let headerData = []
|
|
||||||
if (localRequest.request.header) {
|
|
||||||
headerData = dataFromLocalURLEncode(localRequest.request.header)
|
|
||||||
.map((header) => ({ key: header.key, value: header.value, enabled: header.enabled, description: header.description }))
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = {
|
|
||||||
// owner: '8119550',
|
|
||||||
// lastUpdatedBy: '8119550',
|
|
||||||
// lastRevision: 32526900683,
|
|
||||||
// folder: 'dfa47710-b3d3-4a2c-bbc8-fbd25ad12244',
|
|
||||||
// collection: 'fa89c950-c947-4061-9d13-fb18d7c6bc51',
|
|
||||||
|
|
||||||
id: localRequest.id, //
|
|
||||||
name: localRequest.name, //
|
|
||||||
|
|
||||||
dataMode, //
|
|
||||||
data, //
|
|
||||||
auth: localRequest.request.auth, //
|
|
||||||
events: localRequest.events, //
|
|
||||||
//
|
|
||||||
rawModeData, // body os request
|
|
||||||
//
|
|
||||||
descriptionFormat: localRequest.descriptionFormat, // it can be in either ``html`` or ``markdown`` formats.
|
|
||||||
description: localRequest.request.description, //
|
|
||||||
// Headers
|
|
||||||
headers: null,
|
|
||||||
headerData,
|
|
||||||
//
|
|
||||||
variables: localRequest.variables,
|
|
||||||
method: localRequest.request.method, //
|
|
||||||
|
|
||||||
// Path Variables
|
|
||||||
pathVariables: pathVariableData, //
|
|
||||||
pathVariableData, //
|
|
||||||
//
|
|
||||||
url, //
|
|
||||||
preRequestScript: localRequest.preRequestScript,
|
|
||||||
tests: localRequest.tests,
|
|
||||||
currentHelper: localRequest.currentHelper,
|
|
||||||
helperAttributes: localRequest.helperAttributes,
|
|
||||||
queryParams, //
|
|
||||||
|
|
||||||
protocolProfileBehavior: localRequest.protocolProfileBehavior,
|
|
||||||
dataDisabled: localRequest.dataDisabled,
|
|
||||||
responses_order: localRequest.responses_order
|
|
||||||
|
|
||||||
// createdAt: '2023-09-12T16:25:20.000Z',
|
|
||||||
// updatedAt: '2023-09-12T16:25:23.000Z',
|
|
||||||
// dataOptions: {{"raw":{}}},
|
|
||||||
}
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseFromLocal = (localResponse) => {
|
|
||||||
const headers = localResponse.header.map((item) => ({ key: item.key, value: item.value }))
|
|
||||||
const response = {
|
|
||||||
// owner: '8119550',
|
|
||||||
// lastUpdatedBy: '8119550',
|
|
||||||
// lastRevision: 32546597265,
|
|
||||||
// request: '331bbc94-5425-46f3-8c02-31c353d2ced8',
|
|
||||||
|
|
||||||
id: localResponse.id, //
|
|
||||||
name: localResponse.name, //
|
|
||||||
status: localResponse.status, //
|
|
||||||
responseCode: {
|
|
||||||
code: localResponse.code, //
|
|
||||||
name: localResponse.status, //
|
|
||||||
detail: ''
|
|
||||||
},
|
|
||||||
// time: null,
|
|
||||||
headers, //
|
|
||||||
cookies: [],
|
|
||||||
mime: null,
|
|
||||||
text: localResponse.body, //
|
|
||||||
language: 'Text', //
|
|
||||||
rawDataType: 'text'//
|
|
||||||
// requestObject: null,
|
|
||||||
// createdAt: '2023-09-13T14:53:05.000Z',
|
|
||||||
// updatedAt: '2023-09-13T14:53:05.000Z'
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataFromLocalURLEncode = (localFormData) => {
|
|
||||||
const data = []
|
|
||||||
for (const param of localFormData) {
|
|
||||||
const item = {
|
|
||||||
key: param.key,
|
|
||||||
description: param.description,
|
|
||||||
value: param.value,
|
|
||||||
enabled: !param.disabled
|
|
||||||
}
|
|
||||||
data.push(item)
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
requestFromLocal,
|
|
||||||
responseFromLocal
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
const uuid = require('uuid')
|
|
||||||
const NAMESPACE = '33c4e6fc-44cb-4190-b19f-4a02821bc8c3'
|
|
||||||
|
|
||||||
const genID = (objectJSON = null) => {
|
|
||||||
if (objectJSON) {
|
|
||||||
return uuid.v5(objectJSON, NAMESPACE)
|
|
||||||
} else {
|
|
||||||
return uuid.v4()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort 2 objects by name
|
|
||||||
const byName = (a, b) => {
|
|
||||||
if (a.name < b.name) {
|
|
||||||
return -1
|
|
||||||
} else if (a.name > b.name) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort two object by priority
|
|
||||||
const byPriority = (a, b) => {
|
|
||||||
if (a['x-box-priority']) {
|
|
||||||
return -1
|
|
||||||
} else if (b['x-box-priority']) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
GenID: genID,
|
|
||||||
ByName: byName,
|
|
||||||
ByPriority: byPriority
|
|
||||||
// logAxiosError
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
const fs = require('fs')
|
|
||||||
const { deployIncremental } = require('./DeployIncremental')
|
|
||||||
|
|
||||||
const release = async () => {
|
|
||||||
let privateRemoteCollectionId = '23836355-c5640083-7523-4ad7-9f92-b23e079cbb7b'
|
|
||||||
let publicRemoteCollectionId = '23836355-6224d51a-d924-4c39-a58f-6970735aac8e'
|
|
||||||
let localCollection = JSON.parse(fs.readFileSync(`C:\\git\\api-specs\\postman\\collections\\sailpoint-api-v3.json`).toString())
|
|
||||||
await deployIncremental(privateRemoteCollectionId, localCollection, publicRemoteCollectionId)
|
|
||||||
}
|
|
||||||
|
|
||||||
release()
|
|
||||||
131
postman-script/partialUpdate/package-lock.json
generated
131
postman-script/partialUpdate/package-lock.json
generated
@@ -1,131 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "partialupdate",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "partialupdate",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"axios": "^1.6.5",
|
|
||||||
"dotenv": "^16.3.1",
|
|
||||||
"uuid": "^9.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/asynckit": {
|
|
||||||
"version": "0.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
|
||||||
},
|
|
||||||
"node_modules/axios": {
|
|
||||||
"version": "1.6.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
|
|
||||||
"integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
|
|
||||||
"dependencies": {
|
|
||||||
"follow-redirects": "^1.15.4",
|
|
||||||
"form-data": "^4.0.0",
|
|
||||||
"proxy-from-env": "^1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/combined-stream": {
|
|
||||||
"version": "1.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
|
||||||
"dependencies": {
|
|
||||||
"delayed-stream": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/delayed-stream": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/dotenv": {
|
|
||||||
"version": "16.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
|
||||||
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/motdotla/dotenv?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/follow-redirects": {
|
|
||||||
"version": "1.15.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
|
||||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"debug": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/form-data": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
|
||||||
"dependencies": {
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"combined-stream": "^1.0.8",
|
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mime-db": {
|
|
||||||
"version": "1.52.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mime-types": {
|
|
||||||
"version": "2.1.35",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "1.52.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/proxy-from-env": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
|
||||||
},
|
|
||||||
"node_modules/uuid": {
|
|
||||||
"version": "9.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
|
||||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
|
||||||
"funding": [
|
|
||||||
"https://github.com/sponsors/broofa",
|
|
||||||
"https://github.com/sponsors/ctavan"
|
|
||||||
],
|
|
||||||
"bin": {
|
|
||||||
"uuid": "dist/bin/uuid"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "partialupdate",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "partial updates to postman files",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"axios": "^1.6.5",
|
|
||||||
"dotenv": "^16.3.1",
|
|
||||||
"uuid": "^9.0.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,236 +0,0 @@
|
|||||||
require('dotenv').config()
|
|
||||||
const axios = require('axios')
|
|
||||||
|
|
||||||
class Collection {
|
|
||||||
constructor (collectionId) {
|
|
||||||
this.collectionId = collectionId
|
|
||||||
this.apiKey = process.env.POSTMAN_API_KEY
|
|
||||||
this.axios = axios.create({
|
|
||||||
timeout: 1000 * 60, // 60 seconds
|
|
||||||
headers: { 'Content-Type': 'application/json', 'X-Api-Key': this.apiKey }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async get () {
|
|
||||||
return await this.axios.get(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}`
|
|
||||||
, { timeout: 1000 * 60 * 5 } // 5 minutes
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error getting collection ${this.collectionId}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async merge (destinationCollectionId, strategy = 'updateSourceWithDestination') {
|
|
||||||
return await this.axios.post(
|
|
||||||
'https://api.getpostman.com/collections/merge',
|
|
||||||
{ source: this.collectionId, destination: destinationCollectionId, strategy },
|
|
||||||
{ timeout: 1000 * 60 * 5 } // 5 minutes
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error merging collection from ${this.collectionId} to ${destinationCollectionId}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async update (collection) {
|
|
||||||
return await this.axios.put(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}`,
|
|
||||||
collection,
|
|
||||||
{ timeout: 1000 * 60 * 5 } // 5 minutes
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error updating collection ${collection.id}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Folder {
|
|
||||||
constructor (collectionId) {
|
|
||||||
this.collectionId = collectionId
|
|
||||||
this.apiKey = process.env.POSTMAN_API_KEY
|
|
||||||
this.axios = axios.create({
|
|
||||||
timeout: 1000 * 60, // 60 seconds
|
|
||||||
headers: { 'Content-Type': 'application/json', 'X-Api-Key': this.apiKey }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async get (folderId) {
|
|
||||||
return await this.axios.get(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/folders/${folderId}`
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error getting folder ${folderId}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async create (folder) {
|
|
||||||
return await this.axios.post(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/folders`,
|
|
||||||
folder
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error creating folder ${folder.Id}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async update (folderId, folder) {
|
|
||||||
return await this.axios.put(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/folders/${folderId}`,
|
|
||||||
folder
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error updating folder ${folder.Id}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete (folderId) {
|
|
||||||
return await this.axios.delete(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/folders/${folderId}`
|
|
||||||
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error deleting folder ${folderId}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} // class Folder
|
|
||||||
|
|
||||||
class Request {
|
|
||||||
constructor (collectionId) {
|
|
||||||
this.collectionId = collectionId
|
|
||||||
this.apiKey = process.env.POSTMAN_API_KEY
|
|
||||||
this.axios = axios.create({
|
|
||||||
timeout: 1000 * 60, // 60 seconds
|
|
||||||
headers: { 'Content-Type': 'application/json', 'X-Api-Key': this.apiKey }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async get (requestId) {
|
|
||||||
return await this.axios.get(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/requests/${requestId}`
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error getting request ${requestId}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async create (request, folderId) {
|
|
||||||
return await this.axios.post(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/requests`,
|
|
||||||
request,
|
|
||||||
{ params: { folder: folderId } }
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error creating request ${request.id}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async update (requestId, request) {
|
|
||||||
return await this.axios.put(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/requests/${requestId}`,
|
|
||||||
request
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error updating request ${request.id}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete (requestId) {
|
|
||||||
return await this.axios.delete(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/requests/${requestId}`
|
|
||||||
|
|
||||||
).then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Error deleting request ${requestId}: ${response.status} ${response.statusText}`)
|
|
||||||
} else {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} // class Request
|
|
||||||
|
|
||||||
class Response {
|
|
||||||
constructor (collectionId) {
|
|
||||||
this.collectionId = collectionId
|
|
||||||
this.apiKey = process.env.POSTMAN_API_KEY
|
|
||||||
this.axios = axios.create({
|
|
||||||
timeout: 1000 * 60, // 60 seconds
|
|
||||||
headers: { 'Content-Type': 'application/json', 'X-Api-Key': this.apiKey }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async get (responseId) {
|
|
||||||
return await this.axios.get(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/responses/${responseId}`
|
|
||||||
).then(function (axiosResp) {
|
|
||||||
if (axiosResp.status !== 200) {
|
|
||||||
throw new Error(`Error getting response ${responseId}: ${axiosResp.status} ${axiosResp.statusText}`)
|
|
||||||
} else {
|
|
||||||
return axiosResp.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async create (response, requestId) {
|
|
||||||
return await this.axios.post(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/responses`,
|
|
||||||
response,
|
|
||||||
{ params: { request: requestId } }
|
|
||||||
).then(function (axiosResp) {
|
|
||||||
if (axiosResp.status !== 200) {
|
|
||||||
throw new Error(`Error creating response ${response.id}: ${axiosResp.status} ${axiosResp.statusText}`)
|
|
||||||
} else {
|
|
||||||
return axiosResp.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete (responseId) {
|
|
||||||
return await this.axios.delete(
|
|
||||||
`https://api.getpostman.com/collections/${this.collectionId}/responses/${responseId}`
|
|
||||||
|
|
||||||
).then(function (axiosResp) {
|
|
||||||
if (axiosResp.status !== 200) {
|
|
||||||
throw new Error(`Error deleting response ${responseId}: ${axiosResp.status} ${axiosResp.statusText}`)
|
|
||||||
} else {
|
|
||||||
return axiosResp.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} // class Response
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Collection,
|
|
||||||
Folder,
|
|
||||||
Request,
|
|
||||||
Response
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
const requestFromLocal = (localRequest, responses) => {
|
const requestFromLocal = (localRequest, responses) => {
|
||||||
// console.log('localRequest', localRequest)
|
// console.log('localRequest', localRequest)
|
||||||
let url = localRequest.request.url.host + '/' + localRequest.request.url.path
|
let url = localRequest.request.url.host + '/' + localRequest.request.url.path.join('/')
|
||||||
|
|
||||||
let data = []
|
let data = []
|
||||||
let dataMode = null
|
let dataMode = null
|
||||||
@@ -48,6 +48,12 @@ const requestFromLocal = (localRequest, responses) => {
|
|||||||
.map((header) => ({ key: header.key, value: header.value, enabled: header.enabled, description: header.description }))
|
.map((header) => ({ key: header.key, value: header.value, enabled: header.enabled, description: header.description }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let headers = []
|
||||||
|
if (localRequest.request.header) {
|
||||||
|
headers = dataFromLocalURLEncode(localRequest.request.header)
|
||||||
|
.map((header) => ({ key: header.key, value: header.value, description: header.description }))
|
||||||
|
}
|
||||||
|
|
||||||
if (JSON.stringify(localRequest.request.description) === '{}') {
|
if (JSON.stringify(localRequest.request.description) === '{}') {
|
||||||
localRequest.request.description = ''
|
localRequest.request.description = ''
|
||||||
}
|
}
|
||||||
@@ -70,9 +76,9 @@ const requestFromLocal = (localRequest, responses) => {
|
|||||||
rawModeData, // body os request
|
rawModeData, // body os request
|
||||||
//
|
//
|
||||||
descriptionFormat: localRequest.descriptionFormat, // it can be in either ``html`` or ``markdown`` formats.
|
descriptionFormat: localRequest.descriptionFormat, // it can be in either ``html`` or ``markdown`` formats.
|
||||||
description: localRequest.request.description.content ? localRequest.request.description.content : localRequest.request.description, //
|
description: localRequest.request.description && localRequest.request.description.content ? localRequest.request.description.content : localRequest.request.description, //
|
||||||
// Headers
|
// Headers
|
||||||
headers: localRequest.request.header,
|
headers: headers,
|
||||||
headerData,
|
headerData,
|
||||||
//
|
//
|
||||||
variables: localRequest.variables,
|
variables: localRequest.variables,
|
||||||
@@ -102,7 +108,17 @@ const requestFromLocal = (localRequest, responses) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const responseFromLocal = (localResponse, requestObject) => {
|
const responseFromLocal = (localResponse, requestObject) => {
|
||||||
const headers = localResponse.header.map((item) => ({ key: item.key, value: item.value }))
|
const headers = localResponse.header
|
||||||
|
.map((item) => ({ key: item.key, value: item.value }))
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (a.key < b.key) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.key > b.key) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
const response = {
|
const response = {
|
||||||
// owner: '8119550',
|
// owner: '8119550',
|
||||||
// lastUpdatedBy: '8119550',
|
// lastUpdatedBy: '8119550',
|
||||||
@@ -121,7 +137,7 @@ const requestFromLocal = (localRequest, responses) => {
|
|||||||
headers, //
|
headers, //
|
||||||
cookies: [],
|
cookies: [],
|
||||||
mime: null,
|
mime: null,
|
||||||
text: localResponse.body, //
|
text: localResponse.body ? localResponse.body : '', //
|
||||||
language: 'json', //
|
language: 'json', //
|
||||||
rawDataType: 'text',//
|
rawDataType: 'text',//
|
||||||
requestObject: requestObject,
|
requestObject: requestObject,
|
||||||
@@ -135,11 +151,18 @@ const requestFromLocal = (localRequest, responses) => {
|
|||||||
const dataFromLocalURLEncode = (localFormData) => {
|
const dataFromLocalURLEncode = (localFormData) => {
|
||||||
const data = []
|
const data = []
|
||||||
for (const param of localFormData) {
|
for (const param of localFormData) {
|
||||||
|
// check if param.key is a number
|
||||||
|
if (!param.key || !isNaN(param.key)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (JSON.stringify(param.description) === '{}') {
|
||||||
|
param.description = ''
|
||||||
|
}
|
||||||
const item = {
|
const item = {
|
||||||
key: param.key,
|
key: param.key,
|
||||||
description: param.description,
|
description: param.description && param.description.content ? param.description.content : param.description,
|
||||||
value: param.value,
|
value: param.value,
|
||||||
enabled: !param.disabled
|
enabled: false
|
||||||
}
|
}
|
||||||
data.push(item)
|
data.push(item)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const release = async () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// This function just cleans up the variables so they match what is returned in postman
|
// This step just cleans up the variables so they match what is returned in postman
|
||||||
for (let variable of localCollection.variable) {
|
for (let variable of localCollection.variable) {
|
||||||
if (variable.type) {
|
if (variable.type) {
|
||||||
delete variable.type
|
delete variable.type
|
||||||
@@ -41,11 +41,35 @@ const release = async () => {
|
|||||||
// add any missing folders
|
// add any missing folders
|
||||||
for (let item of localCollection.item) {
|
for (let item of localCollection.item) {
|
||||||
let folder = getMatchingFolder(item, remoteCollection.collection.item)
|
let folder = getMatchingFolder(item, remoteCollection.collection.item)
|
||||||
|
if (checkIfDifferent(folder.description, item.description)) {
|
||||||
|
console.log(`updating folder ${folder.name}`)
|
||||||
|
await new pmAPI.Folder(publicRemoteCollectionId).update(folder.id, { description: item.description })
|
||||||
|
console.log(`updated folder ${folder.name}`)
|
||||||
|
}
|
||||||
if (folder == null) {
|
if (folder == null) {
|
||||||
await updateEntireFolder(item)
|
await updateEntireFolder(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete any folders that are no longer in the collection
|
||||||
|
for (let folder of remoteCollection.collection.item) {
|
||||||
|
let localFolder = getMatchingFolder(folder, localCollection.item)
|
||||||
|
if (localFolder == null) {
|
||||||
|
await new pmAPI.Folder(publicRemoteCollectionId).delete(folder.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete any requests that are no longer in the collection
|
||||||
|
for (let folder of remoteCollection.collection.item) {
|
||||||
|
let localFolder = getMatchingFolder(folder, localCollection.item)
|
||||||
|
for (let items of folder.item) {
|
||||||
|
let remoteRequest = getMatchingRequest(items, localFolder.item)
|
||||||
|
if (remoteRequest == null) {
|
||||||
|
await new pmAPI.Request(publicRemoteCollectionId).delete(items.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// update any requests that have changed
|
// update any requests that have changed
|
||||||
for (let item of localCollection.item) {
|
for (let item of localCollection.item) {
|
||||||
let folder = getMatchingFolder(item, remoteCollection.collection.item)
|
let folder = getMatchingFolder(item, remoteCollection.collection.item)
|
||||||
@@ -54,7 +78,6 @@ const release = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(remoteCollection)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +92,7 @@ function getMatchingFolder(localFolder, remoteFolders) {
|
|||||||
|
|
||||||
function getMatchingRequest(localRequest, remoteRequests) {
|
function getMatchingRequest(localRequest, remoteRequests) {
|
||||||
for (let request of remoteRequests) {
|
for (let request of remoteRequests) {
|
||||||
if (request.name === localRequest.name) {
|
if (request.name === localRequest.name && request.request.method === localRequest.request.method && localRequest.request.url.host + '/' + localRequest.request.url.path.join('/') === request.request.url.host + '/' + request.request.url.path.join('/')) {
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,6 +100,9 @@ function getMatchingRequest(localRequest, remoteRequests) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildRequestBody(items) {
|
function buildRequestBody(items) {
|
||||||
|
if (items === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
let responses = []
|
let responses = []
|
||||||
for (let response of items.response) {
|
for (let response of items.response) {
|
||||||
@@ -87,12 +113,87 @@ function buildRequestBody(items) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function checkIfDifferent(source, dest) {
|
function checkIfDifferent(source, dest) {
|
||||||
if (JSON.stringify(source) === JSON.stringify(dest)) {
|
removeIdFields(source)
|
||||||
|
removeIdFields(dest)
|
||||||
|
if (isDeepEqual(source, dest)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeIdFields(obj) {
|
||||||
|
if (typeof obj !== 'object') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (obj === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Check if the current object has the 'id' property
|
||||||
|
if (obj.hasOwnProperty('id')) {
|
||||||
|
delete obj.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively call removeIdFields on each property if it's an object
|
||||||
|
for (let key in obj) {
|
||||||
|
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
||||||
|
removeIdFields(obj[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNullorEmpty(obj) {
|
||||||
|
if (obj === null || obj === '' || obj === undefined) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDeepEqual(obj1, obj2) {
|
||||||
|
if (areValuesEqual(obj1, obj2)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 == null || obj2 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keys1 = Object.keys(obj1);
|
||||||
|
const keys2 = Object.keys(obj2);
|
||||||
|
|
||||||
|
if (keys1.length !== keys2.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let key of keys1) {
|
||||||
|
const val1 = obj1[key];
|
||||||
|
const val2 = obj2[key];
|
||||||
|
const areObjects = isObject(val1) && isObject(val2);
|
||||||
|
if (
|
||||||
|
areObjects && !isDeepEqual(val1, val2) ||
|
||||||
|
(!areObjects && !areValuesEqual(val1, val2))
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function areValuesEqual(val1, val2) {
|
||||||
|
if (isNullorEmpty(val1) && isNullorEmpty(val2)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (val1 === val2) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function isObject(object) {
|
||||||
|
return object != null && typeof object === 'object';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function updateEntireFolder(item, folderId) {
|
async function updateEntireFolder(item, folderId) {
|
||||||
if (item.item && !folderId) {
|
if (item.item && !folderId) {
|
||||||
@@ -118,8 +219,12 @@ async function updateRequestsInFolder(item, folderId, remoteItem) {
|
|||||||
let postmanRequestBody = buildRequestBody(items)
|
let postmanRequestBody = buildRequestBody(items)
|
||||||
let remotePostmanBody = buildRequestBody(remoteRequest)
|
let remotePostmanBody = buildRequestBody(remoteRequest)
|
||||||
if (checkIfDifferent(postmanRequestBody, remotePostmanBody)) {
|
if (checkIfDifferent(postmanRequestBody, remotePostmanBody)) {
|
||||||
let newRequestDelete = await new pmAPI.Request(publicRemoteCollectionId).delete(remoteRequest.id)
|
if (remoteRequest) {
|
||||||
console.log(`deleting request ${newRequestDelete.data.id}`)
|
console.log(`deleting request ${remoteRequest.name}`)
|
||||||
|
let newRequestDelete = await new pmAPI.Request(publicRemoteCollectionId).delete(remoteRequest.id)
|
||||||
|
console.log(`deleted request ${newRequestDelete.data.id}`)
|
||||||
|
}
|
||||||
|
postmanRequestBody = buildRequestBody(items)
|
||||||
let newRequest = await new pmAPI.Request(publicRemoteCollectionId).create(postmanRequestBody, folderId)
|
let newRequest = await new pmAPI.Request(publicRemoteCollectionId).create(postmanRequestBody, folderId)
|
||||||
console.log(`creating request ${newRequest.data.name}`)
|
console.log(`creating request ${newRequest.data.name}`)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -31,14 +31,13 @@ class Collection {
|
|||||||
})
|
})
|
||||||
axiosRetry(this.axios, {
|
axiosRetry(this.axios, {
|
||||||
retries: 10,
|
retries: 10,
|
||||||
retryDelay: axiosRetry.exponentialDelay,
|
|
||||||
retryCondition: (error) => {
|
retryCondition: (error) => {
|
||||||
console.log('error, retrying')
|
console.log('error, retrying')
|
||||||
return error.code === 'ECONNRESET' || error.code === 'ECONNABORTED' || axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response.status === 429
|
return error.code === 'ECONNRESET' || error.code === 'ECONNABORTED' || axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response.status === 429
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.axios.interceptors.response.use(response => response, handleError);
|
//this.axios.interceptors.response.use(response => response, handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -94,13 +93,13 @@ class Folder {
|
|||||||
})
|
})
|
||||||
axiosRetry(this.axios, {
|
axiosRetry(this.axios, {
|
||||||
retries: 10,
|
retries: 10,
|
||||||
retryDelay: axiosRetry.exponentialDelay,
|
//retryDelay: axiosRetry.exponentialDelay,
|
||||||
retryCondition: (error) => {
|
retryCondition: (error) => {
|
||||||
console.log('error, retrying')
|
console.log('error, retrying')
|
||||||
return error.code === 'ECONNRESET' || error.code === 'ECONNABORTED' || axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response.status === 429
|
return error.code === 'ECONNRESET' || error.code === 'ECONNABORTED' || axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response.status === 429
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.axios.interceptors.response.use(response => response, handleError);
|
//this.axios.interceptors.response.use(response => response, handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get (folderId) {
|
async get (folderId) {
|
||||||
@@ -165,13 +164,13 @@ class Request {
|
|||||||
})
|
})
|
||||||
axiosRetry(this.axios, {
|
axiosRetry(this.axios, {
|
||||||
retries: 10,
|
retries: 10,
|
||||||
retryDelay: axiosRetry.exponentialDelay,
|
//retryDelay: axiosRetry.exponentialDelay,
|
||||||
retryCondition: (error) => {
|
retryCondition: (error) => {
|
||||||
console.log('error, retrying')
|
console.log('error, retrying')
|
||||||
return error.code === 'ECONNRESET' || error.code === 'ECONNABORTED' || axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response.status === 429
|
return error.code === 'ECONNRESET' || error.code === 'ECONNABORTED' || axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response.status === 429
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.axios.interceptors.response.use(response => response, handleError);
|
//this.axios.interceptors.response.use(response => response, handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get (requestId) {
|
async get (requestId) {
|
||||||
@@ -237,13 +236,13 @@ class Response {
|
|||||||
})
|
})
|
||||||
axiosRetry(this.axios, {
|
axiosRetry(this.axios, {
|
||||||
retries: 10,
|
retries: 10,
|
||||||
retryDelay: axiosRetry.exponentialDelay,
|
//retryDelay: axiosRetry.exponentialDelay,
|
||||||
retryCondition: (error) => {
|
retryCondition: (error) => {
|
||||||
console.log('error, retrying')
|
console.log('error, retrying')
|
||||||
return error.code === 'ECONNRESET' || error.code === 'ECONNABORTED' || axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response.status === 429
|
return error.code === 'ECONNRESET' || error.code === 'ECONNABORTED' || axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response.status === 429
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.axios.interceptors.response.use(response => response, handleError);
|
//this.axios.interceptors.response.use(response => response, handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get (responseId) {
|
async get (responseId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user