public static async migrateProjectAsync()

in Composer/packages/server/src/services/project.ts [468:622]


  public static async migrateProjectAsync(req: Request, jobId: string) {
    const { oldProjectId, name, description, location, storageId, runtimeType, runtimeLanguage } = req.body;

    const user = await ExtensionContext.getUserFromRequest(req);

    try {
      const locationRef = getLocationRef(location, storageId, name);

      await BotProjectService.cleanProject(locationRef);

      log('Downloading adaptive generator');

      // Update status for polling
      BackgroundProcessManager.updateProcess(jobId, 202, formatMessage('Getting template'));
      const baseGenerator = '@microsoft/generator-bot-adaptive';
      const latestVersion = await getLatestGeneratorVersion(baseGenerator);

      log(`Using version ${latestVersion} of ${baseGenerator} for migration`);
      const newProjRef = await AssetService.manager.copyRemoteProjectTemplateToV2(
        baseGenerator,
        latestVersion, // use the @latest version
        name,
        locationRef,
        jobId,
        runtimeType,
        runtimeLanguage,
        {
          applicationSettingsDirectory: 'settings',
        },
        user
      );

      // update project ref to point at newly created folder
      newProjRef.path = `${newProjRef.path}/${name}`;

      BackgroundProcessManager.updateProcess(jobId, 202, formatMessage('Migrating data'));

      log('Migrating files...');

      const originalProject = await BotProjectService.getProjectById(oldProjectId, user);

      if (originalProject.settings) {
        const originalFiles = originalProject.getProject().files;

        // pass in allowPartialBots = true so that this project can be opened even though
        // it doesn't yet have a root dialog...
        const id = await BotProjectService.openProject(newProjRef, user, true, { allowPartialBots: true });
        const currentProject = await BotProjectService.getProjectById(id, user);

        // add all original files to new project
        for (let f = 0; f < originalFiles.length; f++) {
          // exclude the schema files, so we start from scratch
          if (!isSchema(originalFiles[f].name)) {
            await currentProject.migrateFile(
              originalFiles[f].name,
              originalFiles[f].content,
              originalProject.rootDialogId
            );
          }
        }
        const newSettings: DialogSetting = {
          ...currentProject.settings,
          runtimeSettings: {
            components: [],
            features: {
              showTyping: originalProject.settings?.feature?.UseShowTypingMiddleware || false,
              useInspection: originalProject.settings?.feature?.UseInspectionMiddleware || false,
              removeRecipientMentions: originalProject.settings?.feature?.RemoveRecipientMention || false,
              setSpeak: originalProject.settings?.feature?.useSetSpeakMiddleware
                ? { voiceFontName: 'en-US-AriaNeural', fallbackToTextForSpeechIfEmpty: true }
                : undefined,
              blobTranscript: originalProject.settings?.blobStorage?.connectionString
                ? {
                    connectionString: originalProject.settings.blobStorage.connectionString,
                    containerName: originalProject.settings.blobStorage.container,
                  }
                : {},
            },
            telemetry: {
              options: { instrumentationKey: originalProject.settings?.applicationInsights?.InstrumentationKey },
            },
            skills: {
              allowedCallers: originalProject.settings?.skillConfiguration?.allowedCallers,
            },
            storage: originalProject.settings?.cosmosDb?.authKey ? 'CosmosDbPartitionedStorage' : undefined,
          },
          CosmosDbPartitionedStorage: originalProject.settings?.cosmosDb?.authKey
            ? originalProject.settings.cosmosDb
            : undefined,
          luis: { ...originalProject.settings.luis },
          luFeatures: { ...originalProject.settings.luFeatures },
          publishTargets: originalProject.settings.publishTargets?.map((target) => {
            if (target.type === 'azureFunctionsPublish') target.type = 'azurePublish';
            return target;
          }),
          qna: { ...originalProject.settings.qna },
          downsampling: { ...originalProject.settings.downsampling },
          skill: { ...originalProject.settings.skill },
          speech: { ...originalProject.settings.speech },
          defaultLanguage: originalProject.settings.defaultLanguage,
          languages: originalProject.settings.languages,
          customFunctions: originalProject.settings.customFunctions ?? [],
          importedLibraries: [],
          MicrosoftAppId: originalProject.settings.MicrosoftAppId,

          runtime: currentProject.settings?.runtime
            ? { ...currentProject.settings.runtime }
            : {
                customRuntime: true,
                path: '../',
                key: 'adaptive-runtime-dotnet-webapp',
                command: `dotnet run --project ${name}.csproj`,
              },
        };

        log('Update settings...');

        // adjust settings from old format to new format
        await currentProject.updateEnvSettings(newSettings);

        log('Copy boilerplate...');
        await AssetService.manager.copyBoilerplate(currentProject.dataDir, currentProject.fileStorage);

        log('Update bot info...');
        await currentProject.updateBotInfo(name, description, true);

        const runtime = ExtensionContext.getRuntimeByProject(currentProject);
        const runtimePath = currentProject.getRuntimePath();
        if (runtimePath) {
          // install all dependencies and build the app
          BackgroundProcessManager.updateProcess(jobId, 202, formatMessage('Building runtime'));
          log('Build new runtime...');
          await runtime.build(runtimePath, currentProject);
        }

        await ejectAndMerge(currentProject, jobId);

        const project = currentProject.getProject();

        log('Project created successfully.');
        BackgroundProcessManager.updateProcess(jobId, 200, 'Migrated successfully', {
          id,
          ...project,
        });
      } else {
        BackgroundProcessManager.updateProcess(jobId, 500, 'Could not find source project to migrate.');
      }
    } catch (err) {
      BackgroundProcessManager.updateProcess(jobId, 500, err instanceof Error ? err.message : err, err);
      TelemetryService.trackEvent('CreateNewBotProjectCompleted', {
        template: '@microsoft/generator-microsoft-bot-adaptive',
        status: 500,
      });
    }
  }