text: /// <summary>
  /// Names of app bundles on this project
  /// </summary>
  router.get('/appbundles', async /*GetLocalBundles*/ (req, res) => {
      // this folder is placed under the public folder, which may expose the bundles
      // but it was defined this way so it be published on most hosts easily
      let bundles = await Utils.findFiles(Utils.LocalBundlesFolder, '.zip');
      bundles = bundles.map((fn) => _path.basename(fn, '.zip'));
      res.json(bundles);
  });
  
  /// <summary>
  /// Return a list of available engines
  /// </summary>
  router.get('/forge/designautomation/engines', async /*GetAvailableEngines*/ (req, res) => {
      let that = this;
      let Allengines = [];
      let paginationToken = null;
      try {
          const api = await Utils.dav3API(req.oauth_token);
          while (true) {
              let engines = await api.getEngines({'page':paginationToken});
              Allengines = Allengines.concat(engines.data)
              if (engines.paginationToken == null) break;
              paginationToken = engines.paginationToken;
          }
          res.json(Allengines.sort()); // return list of engines
      } catch (ex) {
          console.error(ex);
          res.json([]);
      }
  
  });
  
  /// <summary>
  /// Define a new appbundle
  /// </summary>
  router.post('/forge/designautomation/appbundles', async /*CreateAppBundle*/ (req, res) => {
      const appBundleSpecs = req.body;
  
      // basic input validation
      const zipFileName = appBundleSpecs.zipFileName;
      const engineName = appBundleSpecs.engine;
  
      // standard name for this sample
      const appBundleName = zipFileName + 'AppBundle';
  
      // check if ZIP with bundle is here
      const packageZipPath = _path.join(Utils.LocalBundlesFolder, zipFileName + '.zip');
  
      // get defined app bundles
      const api = await Utils.dav3API(req.oauth_token);
      let appBundles = null;
      try {
          appBundles = await api.getAppBundles();
      } catch (ex) {
          console.error(ex);
          return (res.status(500).json({
              diagnostic: 'Failed to get the Bundle list'
          }));
      }
      // check if app bundle is already define
      let newAppVersion = null;
      const qualifiedAppBundleId = `${Utils.NickName}.${appBundleName}+${Utils.Alias}`;
      if (!appBundles.data.includes(qualifiedAppBundleId)) {
          // create an appbundle (version 1)
          // const appBundleSpec = {
          //         package: appBundleName,
          //         engine: engineName,
          //         id: appBundleName,
          //         description: `Description for ${appBundleName}`
          //     };
          const appBundleSpec = dav3.AppBundle.constructFromObject({
              package: appBundleName,
              engine: engineName,
              id: appBundleName,
              description: `Description for ${appBundleName}`
          });
          try {
              newAppVersion = await api.createAppBundle(appBundleSpec);
          } catch (ex) {
              console.error(ex);
              return (res.status(500).json({
                  diagnostic: 'Cannot create new app'
              }));
          }
  
          // create alias pointing to v1
          const aliasSpec = //dav3.Alias.constructFromObject({
          {
              id: Utils.Alias,
              version: 1
          };
          try {
              const newAlias = await api.createAppBundleAlias(appBundleName, aliasSpec);
          } catch (ex) {
              console.error(ex);
              return (res.status(500).json({
                  diagnostic: 'Failed to create an alias'
              }));
          }
      } else {
          // create new version
          const appBundleSpec = //dav3.AppBundle.constructFromObject({
          {
              engine: engineName,
              description: appBundleName
          };
          try {
              newAppVersion = await api.createAppBundleVersion(appBundleName, appBundleSpec);
          } catch (ex) {
              console.error(ex);
              return (res.status(500).json({
                  diagnostic: 'Cannot create new version'
              }));
          }
  
          // update alias pointing to v+1
          const aliasSpec = //dav3.AliasPatch.constructFromObject({
          {
              version: newAppVersion.version
          };
          try {
              const newAlias = await api.modifyAppBundleAlias(appBundleName, Utils.Alias, aliasSpec);
          } catch (ex) {
              console.error(ex);
              return (res.status(500).json({
                  diagnostic: 'Failed to create an alias'
              }));
          }
      }
  
      // upload the zip with .bundle
      try {
          // curl https://bucketname.s3.amazonaws.com/
          // -F key = apps/myApp/myfile.zip
          // -F content-type = application/octet-stream
          // -F policy = eyJleHBpcmF0aW9uIjoiMjAxOC0wNi0yMVQxMzo...(trimmed)
          // -F x-amz-signature = 800e52d73579387757e1c1cd88762...(trimmed)
          // -F x-amz-credential = AKIAIOSFODNN7EXAMPLE/20180621/us-west-2/s3/aws4_request/
          // -F x-amz-algorithm = AWS4-HMAC-SHA256
          // -F x-amz-date = 20180621T091656Z
          // -F file=@E:myfile.zip
          //
          // The ‘file’ field must be at the end, all fields after ‘file’ will be ignored.
          await Utils.uploadFormDataWithFile(
              packageZipPath,
              newAppVersion.uploadParameters.endpointURL,
              newAppVersion.uploadParameters.formData
          );
      } catch (ex) {
          console.error(ex);
          return (res.status(500).json({
              diagnostic: 'Failed to upload bundle on s3'
          }));
      }
  
      res.status(200).json({
          appBundle: qualifiedAppBundleId,
          version: newAppVersion.version
      });
  });
  
  module.exports = router;
  