SOLVED

Testing an updated web part, on a live SharePoint site, without affecting the existing version

%3CLINGO-SUB%20id%3D%22lingo-sub-131490%22%20slang%3D%22en-US%22%3ETesting%20an%20updated%20web%20part%2C%20on%20a%20live%20SharePoint%20site%2C%20without%20affecting%20the%20existing%20version%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-131490%22%20slang%3D%22en-US%22%3E%3CP%3EHi%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EModern%20experience.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EI%20have%20an%20updated%20version%20of%20a%20web%20part%20that%20I%20need%20to%20test%20on%20a%20live%20SharePoint%20site.%20This%20needs%20to%20be%20done%20without%20affecting%20the%20existing%20version%20of%20the%20web%20part.%20The%20workbench%20is%20not%20suitable%20as%20I%20need%20to%20test%20it%20alongside%20existing%20content.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EI%20suppose%20that%20I%20need%20to%20make%20the%20updated%20web%20part%20look%20like%20a%20new%20web%20part%20for%20testing.%20Then%20back%20like%20the%20existing%20web%20part%20when%20it's%20ready%20to%20go%20live%2C%20but%20with%20an%20updated%20version%20number.%20What%20do%20I%20need%20to%20change%20to%20achieve%20this%2C%20or%20is%20there%20a%20better%20way%3F%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EThanks.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-LABS%20id%3D%22lingo-labs-131490%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EDeveloper%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3ESPFx%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EWebPart%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E%3CLINGO-SUB%20id%3D%22lingo-sub-261609%22%20slang%3D%22en-US%22%3ERe%3A%20Testing%20an%20updated%20web%20part%2C%20on%20a%20live%20SharePoint%20site%2C%20without%20affecting%20the%20existing%20version%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-261609%22%20slang%3D%22en-US%22%3E%3CP%3EThanks%20to%20all%20this%20previous%20information%2C%20it%20led%20me%20to%20find%20that%20the%20site%20collection%20app%20catalog%20is%20now%20available%20and%20so%20I%20was%20able%20to%20set%20up%20an%20app%20catalog%20for%20a%20test%20site%20collection%20and%20an%20app%20catalog%20for%20the%20live%20production%20site%20so%20I%20could%20keep%20the%20updated%20web%20part%20in%20the%20separate%20site%20collection%20and%20have%20testers%20test%20without%20impacting%20production%20(we%20still%20have%20to%20duplicate%20some%20of%20the%20list%20data%20that's%20causing%20bugs%20in%20our%20web%20parts%20but%20ShareGate%20can%20help%20in%20some%20cases).%26nbsp%3B%20In%20case%20this%20helps%20anyone%20else%3A%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fsharepoint%2Fdev%2Fgeneral-development%2Fsite-collection-app-catalog%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fsharepoint%2Fdev%2Fgeneral-development%2Fsite-collection-app-catalog%3C%2FA%3E%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-132209%22%20slang%3D%22en-US%22%3ERe%3A%20Testing%20an%20updated%20web%20part%2C%20on%20a%20live%20SharePoint%20site%2C%20without%20affecting%20the%20existing%20version%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-132209%22%20slang%3D%22en-US%22%3E%3CP%3EHi%20%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F8901%22%20target%3D%22_blank%22%3E%40Russell%20Gove%3C%2FA%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EThanks%2C%20I%20did%20what%20you%20suggested%20and%20it%20worked%20perfectly.%26nbsp%3B%20Between%20%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F29128%22%20target%3D%22_blank%22%3E%40Andrew%20Koltyakov%3C%2FA%3Eand%20yourself%20I%20have%20two%20great%20solutions.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-132123%22%20slang%3D%22en-US%22%3ERe%3A%20Testing%20an%20updated%20web%20part%2C%20on%20a%20live%20SharePoint%20site%2C%20without%20affecting%20the%20existing%20version%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-132123%22%20slang%3D%22en-US%22%3E%3CP%3EThe%20Site%20collection%20app%20catalog%20should%20help%20when%20it%20becomes%20available%2C%20but%20until%20then%20you%20can%20deploy%20a%20Test%20version%20of%20the%20App%20to%20the%20app%20catalog%20and%20your%20cdn%20and%20deploy%20the%20test%20version%20to%20your%20test%20site.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3Ein%20package-solution.json%20change%20the%26nbsp%3B%20solution%20name%26nbsp%3B%20to%20test-yourappname%20and%20the%20id%26nbsp%3B%20to%20a%20new%20guid%20and%20the%20zipped%20package%20name%20to%20solution%2Ftest-yourappname.%20This%20will%20make%20the%20app%20unique.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3Ein%20write-manifests.json%20change%20the%20cdnbasepath%20to%20a%20new%20folder%20where%20you%20will%20deploy%20you%20deploy%20your%20test%20code%20so%20that%20it%20does%20not%20interfere%20with%20the%20production%20code.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3Efor%20each%20of%20your%20webparts%26nbsp%3B%20in%20the%20webpart.manifest.json%20change%20the%20Id%20to%20a%20new%20guid.%20You%20cant%20have%20two%20webparts%20in%20a%20tenat%20with%20the%20same%20id.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3Ethen%20gulp%20clean%3B%20gulp%20bundle%20--ship%2C%26nbsp%3B%20gulp%20package-solution%20--ship.%20This%20will%20create%20a%20new%20sppkg%20file%20for%20your%20test%20app%20%2C%20with%20a%20different%20name%20(test-yourappname)%20that%20references%20the%20different%20cdn%20location%20you%20specified%20in%20write-manifests.json.%3C%2FP%3E%0A%3CP%3Enote%3A%20for%20each%20of%20these%20files%2C%20I%20keep%20a%20.production%20and%20a%20.test%20version%26nbsp%3B%20in%20my%20project%20so%20I%20can%20just%20copy%26nbsp%3B%20the%20contents%20from%20either%20file%20when%20I%20am%20ready%20to%20deploy.%20It%20would%20be%20awesome%20if%20we%20could%20have%20gulp%20do%20that%20for%20us!%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3Ethen%26nbsp%3B%20upload%20the%20test%20sppkg%20to%20your%20app%20catalog%26nbsp%3B%20(solution%2Ftest-yourappname.sppkj)%20and%20deploy%20the%20files%20in%20temp%20deploy%20to%20the%20folder%20you%20specified%20in%20write-manifests.json%20to%20your%20cdn.%20You%20can%20then%20deploy%20your%20test%20app%20to%20your%20test%20site%20without%20interfering%20with%20production.%20You%20can%20even%20have%20the%20two%20versions%20running%20side-by-side%20in%20the%20same%20site%20collection%2C%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EIts%20a%20bit%20of%20work%2C%20but%20it%20has%20been%20working%20consistently%20for%20me%20for%20months%20now.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-131771%22%20slang%3D%22en-US%22%3ERe%3A%20Testing%20an%20updated%20web%20part%2C%20on%20a%20live%20SharePoint%20site%2C%20without%20affecting%20the%20existing%20version%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-131771%22%20slang%3D%22en-US%22%3E%3CP%3EHi%20%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F29128%22%20target%3D%22_blank%22%3E%40Andrew%20Koltyakov%3C%2FA%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EThanks%2C%20I'll%20give%20this%20a%20try.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-131522%22%20slang%3D%22en-US%22%3ERe%3A%20Testing%20an%20updated%20web%20part%2C%20on%20a%20live%20SharePoint%20site%2C%20without%20affecting%20the%20existing%20version%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-131522%22%20slang%3D%22en-US%22%3E%3CP%3EHi%26nbsp%3B%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F25364%22%20target%3D%22_blank%22%3E%40Alan%20Trafford%3C%2FA%3E%2C%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EIn%20such%20situations%2C%20we%20use%20local%20workbench%20and%20target%20requests%20to%20SharePoint%20REST%20Proxy.%3C%2FP%3E%0A%3CP%3EThis%20allows%20request%20real%20SharePoint%20data%20within%20local%26nbsp%3Bworkbench%20without%20publishing%3CSPAN%3E%26nbsp%3B%3C%2FSPAN%3Eapp%3CSPAN%3E%26nbsp%3B%3C%2FSPAN%3Eto%20app%20catalog.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EI'm%20going%20to%20write%20a%20detailed%20blog%20post%20how%20this%20can%20be%20done%20(UPD%3A%20Here%20is%20the%20%3CA%20href%3D%22https%3A%2F%2Fwww.linkedin.com%2Fpulse%2Flocal-spfx-workbench-against-real-sharepoint-api-andrew-koltyakov%2F%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Elink%20to%20the%20article%3C%2FA%3E.).%20Meanwhile%20in%20a%20few%20words%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E1.%20Install%20SharePoint%20REST%20Proxy%20in%20SPFx%26nbsp%3Bsolution%20project%3C%2FP%3E%0A%3CPRE%3Enpm%20install%20sp-rest-proxy%20--save-dev%3C%2FPRE%3E%0A%3CP%3E2.%20Create%20%60proxy.js%60%20file%20in%20a%20root%20or%20wherever%20in%20the%20project%20with%20the%20following%20content%3A%3C%2FP%3E%0A%3CPRE%3Econst%20CertificateStore%20%3D%20require('%40microsoft%2Fgulp-core-build-serve%2Flib%2FCertificateStore')%3B%0Aconst%20RestProxy%20%3D%20require('sp-rest-proxy')%3B%0A%0Aconst%20settings%20%3D%20%7B%0A%20%20configPath%3A%20'.%2Fconfig%2Fprivate.json'%2C%0A%20%20port%3A%204323%2C%0A%20%20protocol%3A%20'https'%2C%0A%20%20ssl%3A%20%7B%0A%20%20%20%20cert%3A%20CertificateStore.default.instance.certificateData%2C%0A%20%20%20%20key%3A%20CertificateStore.default.instance.keyData%0A%20%20%7D%0A%7D%3B%0A%0Aconst%20restProxy%20%3D%20new%20RestProxy(settings)%3B%0ArestProxy.serve()%3B%3C%2FPRE%3E%0A%3CP%3E3.%20Create%3CSPAN%3E%26nbsp%3B%60%3C%2FSPAN%3Enpm%60%26nbsp%3Btask%20to%20execute%2C%20e.g.%20%60%22proxy%22%3A%20%22node%20.%2Fproxy%22%60%3C%2FP%3E%0A%3CP%3E4.%20Run%20and%20configure%20proxy's%20connection%20to%20the%20environment%20by%20%60npm%26nbsp%3Brun%20proxy%60%3C%2FP%3E%0A%3CP%3E5.%20Configure%20a%20task%20to%20start%20proxy%20and%20%60gulp%20serve%60%20at%20the%20same%20time%2C%20e.g.%20using%20%60concurrently%60%20or%3CSPAN%3E%26nbsp%3B%60%3C%2FSPAN%3Enpm-run-all%60%3C%2FP%3E%0A%3CP%3E6.%20In%3CSPAN%3E%26nbsp%3B%3C%2FSPAN%3ESPFx%26nbsp%3Bwebpart%26nbsp%3Buse%20detection%20of%20a%20local%20mode%20with%3A%3C%2FP%3E%0A%3CPRE%3Eimport%20%7B%20Environment%2C%20EnvironmentType%20%7D%20from%20'%40microsoft%2Fsp-core-library'%3B%0Aif%20(Environment.type%20%3D%3D%3D%20EnvironmentType.Local)%20%7B%0A%20%20%2F%2F%20Local%20mode%0A%7D%3C%2FPRE%3E%0A%3CP%3E7.%20If%20local%20mode%2C%20target%20request%20to%20proxy's%20endpoint%3C%2FP%3E%0A%3CP%3EExample%20for%20%60sp-pnp-js%60%3A%3C%2FP%3E%0A%3CPRE%3Eif%20(Environment.type%20%3D%3D%3D%20EnvironmentType.Local)%20%7B%0A%20%20this.web%20%3D%20new%20pnp.Web('https%3A%2F%2Flocalhost%3A4323%2Fsites%2Fsite%2Fweb')%3B%0A%7D%20else%20%7B%0A%20%20pnp.setup(%7B%20spfxContext%3A%20this.context%20%7D)%3B%0A%20%20this.web%20%3D%20new%20pnp.Web(this.context.pageContext.web.absoluteUrl)%3B%0A%7D%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EHope%20this%20helps.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-131496%22%20slang%3D%22en-US%22%3ERe%3A%20Testing%20an%20updated%20web%20part%2C%20on%20a%20live%20SharePoint%20site%2C%20without%20affecting%20the%20existing%20version%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-131496%22%20slang%3D%22en-US%22%3E%3CP%3EHi%20Paul%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EI%20could%20create%20a%20trial%2C%20but%20I'd%20also%20like%20to%20know%20how%20to%20do%20it%20by%20changing%20values%20against%20the%20web%20part.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-131495%22%20slang%3D%22en-US%22%3ERe%3A%20Testing%20an%20updated%20web%20part%2C%20on%20a%20live%20SharePoint%20site%2C%20without%20affecting%20the%20existing%20version%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-131495%22%20slang%3D%22en-US%22%3E%3CP%3EHi%20Alan%2C%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3Ebut%20you%20could%20create%20a%20trail%20to%20test%20it%20in%20the%20tenant%20with%20real%20data%20copied.%3C%2FP%3E%0A%3CP%3Ejust%20create%20a%20trial%20account%20here%20you%20can%20also%20test%20your%20scenario%20for%20the%20new%20app.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-131494%22%20slang%3D%22en-US%22%3ERe%3A%20Testing%20an%20updated%20web%20part%2C%20on%20a%20live%20SharePoint%20site%2C%20without%20affecting%20the%20existing%20version%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-131494%22%20slang%3D%22en-US%22%3E%3CP%3EHi%20%40Deleted%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EI%20don't%20have%20a%20test%20tenant%20(I%20did%20have%20an%20Office%20365%20Developer%20account%2C%20but%20it's%20expired)%2C%20but%20I%20do%20have%20a%20test%20site%20collection.%20However%2C%20web%20parts%20are%20installed%20at%20the%20tenant%20level.%20I%20know%20site%20collection%20app%20catalogs%20are%20on%20the%20way%2C%20but%20I%20don't%20think%20they%20are%20released%20yet.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-131493%22%20slang%3D%22en-US%22%3ERe%3A%20Testing%20an%20updated%20web%20part%2C%20on%20a%20live%20SharePoint%20site%2C%20without%20affecting%20the%20existing%20version%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-131493%22%20slang%3D%22en-US%22%3E%3CP%3EWhy%20don't%20you%20use%20a%20tool%20to%20create%20a%20test%20tenant%20and%20copy%20the%20items%20to%20it%20so%20you%20can%20do%20a%20test%20before%20releasing%20it%20on%20a%20production%20tenant.%3C%2FP%3E%3C%2FLINGO-BODY%3E
Contributor

Hi

 

Modern experience.

 

I have an updated version of a web part that I need to test on a live SharePoint site. This needs to be done without affecting the existing version of the web part. The workbench is not suitable as I need to test it alongside existing content.

 

I suppose that I need to make the updated web part look like a new web part for testing. Then back like the existing web part when it's ready to go live, but with an updated version number. What do I need to change to achieve this, or is there a better way?

 

Thanks.

 

9 Replies

Why don't you use a tool to create a test tenant and copy the items to it so you can do a test before releasing it on a production tenant.

Hi @Deleted

 

I don't have a test tenant (I did have an Office 365 Developer account, but it's expired), but I do have a test site collection. However, web parts are installed at the tenant level. I know site collection app catalogs are on the way, but I don't think they are released yet.

Hi Alan,

 

but you could create a trail to test it in the tenant with real data copied.

just create a trial account here you can also test your scenario for the new app.

Hi Paul

 

I could create a trial, but I'd also like to know how to do it by changing values against the web part.

Hi @Alan Trafford,

 

In such situations, we use local workbench and target requests to SharePoint REST Proxy.

This allows request real SharePoint data within local workbench without publishing app to app catalog.

 

I'm going to write a detailed blog post how this can be done (UPD: Here is the link to the article.). Meanwhile in a few words:

 

1. Install SharePoint REST Proxy in SPFx solution project

npm install sp-rest-proxy --save-dev

2. Create `proxy.js` file in a root or wherever in the project with the following content:

const CertificateStore = require('@microsoft/gulp-core-build-serve/lib/CertificateStore');
const RestProxy = require('sp-rest-proxy');

const settings = {
  configPath: './config/private.json',
  port: 4323,
  protocol: 'https',
  ssl: {
    cert: CertificateStore.default.instance.certificateData,
    key: CertificateStore.default.instance.keyData
  }
};

const restProxy = new RestProxy(settings);
restProxy.serve();

3. Create `npm` task to execute, e.g. `"proxy": "node ./proxy"`

4. Run and configure proxy's connection to the environment by `npm run proxy`

5. Configure a task to start proxy and `gulp serve` at the same time, e.g. using `concurrently` or `npm-run-all`

6. In SPFx webpart use detection of a local mode with:

import { Environment, EnvironmentType } from '@microsoft/sp-core-library';
if (Environment.type === EnvironmentType.Local) {
  // Local mode
}

7. If local mode, target request to proxy's endpoint

Example for `sp-pnp-js`:

if (Environment.type === EnvironmentType.Local) {
  this.web = new pnp.Web('https://localhost:4323/sites/site/web');
} else {
  pnp.setup({ spfxContext: this.context });
  this.web = new pnp.Web(this.context.pageContext.web.absoluteUrl);
}

 

Hope this helps.

best response confirmed by Alan Trafford (Contributor)
Solution

The Site collection app catalog should help when it becomes available, but until then you can deploy a Test version of the App to the app catalog and your cdn and deploy the test version to your test site.

 

in package-solution.json change the  solution name  to test-yourappname and the id  to a new guid and the zipped package name to solution/test-yourappname. This will make the app unique.

 

in write-manifests.json change the cdnbasepath to a new folder where you will deploy you deploy your test code so that it does not interfere with the production code.

 

for each of your webparts  in the webpart.manifest.json change the Id to a new guid. You cant have two webparts in a tenat with the same id.

 

then gulp clean; gulp bundle --ship,  gulp package-solution --ship. This will create a new sppkg file for your test app , with a different name (test-yourappname) that references the different cdn location you specified in write-manifests.json.

note: for each of these files, I keep a .production and a .test version  in my project so I can just copy  the contents from either file when I am ready to deploy. It would be awesome if we could have gulp do that for us!

 

then  upload the test sppkg to your app catalog  (solution/test-yourappname.sppkj) and deploy the files in temp deploy to the folder you specified in write-manifests.json to your cdn. You can then deploy your test app to your test site without interfering with production. You can even have the two versions running side-by-side in the same site collection,

 

Its a bit of work, but it has been working consistently for me for months now.

 

 

 

 

Hi @Russell Gove

 

Thanks, I did what you suggested and it worked perfectly.  Between @Andrew Koltyakov and yourself I have two great solutions.

Thanks to all this previous information, it led me to find that the site collection app catalog is now available and so I was able to set up an app catalog for a test site collection and an app catalog for the live production site so I could keep the updated web part in the separate site collection and have testers test without impacting production (we still have to duplicate some of the list data that's causing bugs in our web parts but ShareGate can help in some cases).  In case this helps anyone else: https://docs.microsoft.com/en-us/sharepoint/dev/general-development/site-collection-app-catalog