manifestUrl: formatMessage()

in Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx [118:387]


          manifestUrl: formatMessage(
            'Endpoints should not be empty or endpoint should have endpoint url field in manifest json'
          ),
        };
      }
    } else {
      result.error = { manifestUrl: formatMessage('could not locate manifest.json in zip') };
    }
  } catch (err) {
    // eslint-disable-next-line format-message/literal-pattern
    result.error = { manifestUrl: formatMessage(err.toString()) };
  }
  return result;
};

const validateSKillName = (skillContent, setSkillManifest) => {
  skillContent.name = skillContent.name.replace(skillNameRegex, '');
  setSkillManifest(skillContent);
};
export const getSkillManifest = async (
  projectId: string,
  manifestUrl: string,
  setSkillManifest,
  setFormDataErrors,
  setShowDetail
) => {
  try {
    const { data } = await httpClient.get(`/projects/${projectId}/skill/retrieveSkillManifest`, {
      params: {
        url: manifestUrl,
      },
    });
    validateSKillName(data, setSkillManifest);
  } catch (error) {
    const httpMessage = error?.response?.data?.message;
    const message = httpMessage?.match('Unexpected string in JSON')
      ? formatMessage('Error attempting to parse Skill manifest. There could be an error in its format.')
      : formatMessage('Manifest URL can not be accessed');

    setFormDataErrors({ ...error, manifestUrl: message });
    setShowDetail(false);
  }
};
const getTriggerFormData = (intent: string, content: string): TriggerFormData => ({
  errors: {},
  $kind: 'Microsoft.OnIntent',
  event: '',
  intent: intent,
  triggerPhrases: content,
  regEx: '',
});

const buttonStyle = { root: { marginLeft: '8px' } };

const setAppIdDialogStyles = {
  dialog: {
    title: {
      fontWeight: FontWeights.bold,
      fontSize: FontSizes.size20,
      paddingTop: '14px',
      paddingBottom: '11px',
    },
    subText: {
      fontSize: FontSizes.size14,
      marginBottom: '0px',
    },
  },
  modal: {
    main: {
      maxWidth: '80% !important',
      width: '960px !important',
    },
  },
};
export const CreateSkillModal: React.FC<CreateSkillModalProps> = (props) => {
  const { projectId, addRemoteSkill, addTriggerToRoot, onDismiss } = props;

  const [title, setTitle] = useState(addSkillDialog.SET_APP_ID);
  const [showSetAppIdDialog, setShowSetAppIdDialog] = useState(true);
  const [showIntentSelectDialog, setShowIntentSelectDialog] = useState(false);
  const [formData, setFormData] = useState<{ manifestUrl: string; endpointName: string }>({
    manifestUrl: '',
    endpointName: '',
  });
  const [formDataErrors, setFormDataErrors] = useState<SkillFormDataErrors>({});
  const [skillManifest, setSkillManifest] = useState<any | null>(null);
  const [showDetail, setShowDetail] = useState(false);
  const [createSkillDialogHidden, setCreateSkillDialogHidden] = useState(false);
  const [manifestDirPath, setManifestDirPath] = useState('');
  const [zipContent, setZipContent] = useState({});

  const publishTypes = useRecoilValue(publishTypesState(projectId));
  const { languages, luFeatures, runtime, publishTargets = [], MicrosoftAppId } = useRecoilValue(
    settingsState(projectId)
  );
  const { dialogId } = useRecoilValue(designPageLocationState(projectId));
  const rootDialog = useRecoilValue(rootDialogSelector(projectId));
  const luFiles = useRecoilValue(luFilesSelectorFamily(projectId));
  const { updateRecognizer, setMicrosoftAppProperties, setPublishTargets } = useRecoilValue(dispatcherState);
  const { content: botProjectFile } = useRecoilValue(botProjectFileState(projectId));
  const skillUrls = Object.keys(botProjectFile.skills).map((key) => botProjectFile.skills[key].manifest as string);

  const debouncedValidateManifestURl = useRef(debounce(validateManifestUrl, 500)).current;

  const validationHelpers = {
    formDataErrors,
    setFormDataErrors,
  };

  const options: IDropdownOption[] = useMemo(() => {
    return skillManifest?.endpoints?.map((item) => {
      return {
        key: item.name,
        // eslint-disable-next-line format-message/literal-pattern
        text: formatMessage(item.name),
      };
    });
  }, [skillManifest]);

  const handleManifestUrlChange = (_, currentManifestUrl = '') => {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { manifestUrl, ...rest } = formData;
    debouncedValidateManifestURl(
      {
        formData: { manifestUrl: currentManifestUrl },
        ...validationHelpers,
      },
      skillUrls
    );
    setFormData({
      ...rest,
      manifestUrl: currentManifestUrl,
    });
    setSkillManifest(null);
    setShowDetail(false);
  };

  const validateUrl = useCallback(
    (event) => {
      event.preventDefault();
      setShowDetail(true);
      const localManifestPath = formData.manifestUrl.replace(/\\/g, '/');
      getSkillManifest(projectId, formData.manifestUrl, setSkillManifest, setFormDataErrors, setShowDetail);
      setManifestDirPath(localManifestPath.substring(0, localManifestPath.lastIndexOf('/')));
    },
    [projectId, formData]
  );

  const handleSubmit = async (event, content: string, enable: boolean) => {
    event.preventDefault();
    // add a remote skill, add skill identifier into botProj file
    await addRemoteSkill(formData.manifestUrl, formData.endpointName, zipContent);
    TelemetryClient.track('AddNewSkillCompleted', {
      from: Object.keys(zipContent).length > 0 ? 'zip' : 'url',
    });
    // if added remote skill fail, just not addTrigger to root.
    const skillId = location.href.match(/skill\/([^/]*)/)?.[1];

    //if the root dialog is orchestrator recoginzer type or user chooses orchestrator type before connecting,
    //add the trigger to the root dialog.
    const boundId =
      rootDialog && (rootDialog.luProvider === SDKKinds.OrchestratorRecognizer || enable) ? rootDialog.id : dialogId;

    if (skillId) {
      // add trigger with connect to skill action to root bot
      const triggerFormData = getTriggerFormData(skillManifest.name, content);
      await addTriggerToRoot(boundId, triggerFormData, skillId);
      TelemetryClient.track('AddNewTriggerCompleted', { kind: 'Microsoft.OnIntent' });
    }

    if (enable) {
      // update recognizor type to orchestrator
      await updateRecognizer(projectId, boundId, SDKKinds.OrchestratorRecognizer);
    }
  };

  const handleDismiss = () => {
    setShowSetAppIdDialog(true);
    onDismiss();
  };

  const handleGotoAddSkill = (publishTargetName: string) => {
    const profileTarget = publishTargets.find((target) => target.name === publishTargetName);
    const configuration = JSON.parse(profileTarget?.configuration || '');
    setMicrosoftAppProperties(
      projectId,
      configuration.settings.MicrosoftAppId,
      configuration.settings.MicrosoftAppPassword
    );

    setShowSetAppIdDialog(false);
    setTitle({
      subText: '',
      title: addSkillDialog.SKILL_MANIFEST_FORM.title,
    });
  };

  const handleGotoCreateProfile = () => {
    setCreateSkillDialogHidden(true);
  };

  const handleBrowseButtonUpdate = async (path: string, files: Record<string, JSZipObject>) => {
    // update path in input field
    setFormData({
      ...formData,
      manifestUrl: path,
    });

    const result = await validateLocalZip(files);
    setFormDataErrors(result.error);
    result.path && setManifestDirPath(result.path);
    result.zipContent && setZipContent(result.zipContent);
    if (result.manifestContent) {
      validateSKillName(result.manifestContent, setSkillManifest);
      setShowDetail(true);
    }
  };

  useEffect(() => {
    if (skillManifest?.endpoints) {
      setFormData({
        ...formData,
        endpointName: skillManifest.endpoints[0].name,
      });
    }
  }, [skillManifest]);

  useEffect(() => {
    if (MicrosoftAppId) {
      setShowSetAppIdDialog(false);
      setTitle({
        subText: '',
        title: addSkillDialog.SKILL_MANIFEST_FORM.title,
      });
    }
  }, [MicrosoftAppId]);

  return (
    <Fragment>
      <DialogWrapper
        dialogType={showSetAppIdDialog ? DialogTypes.Customer : DialogTypes.CreateFlow}
        isOpen={!createSkillDialogHidden}
        onDismiss={handleDismiss}
        {...title}
        customerStyle={setAppIdDialogStyles}
      >
        {showSetAppIdDialog && (
          <Fragment>
            <Separator styles={{ root: { marginBottom: '20px' } }} />
            <SetAppId
              projectId={projectId}
              onDismiss={handleDismiss}
              onGotoCreateProfile={handleGotoCreateProfile}
              onNext={handleGotoAddSkill}
            />
          </Fragment>
        )}
        {showIntentSelectDialog && (
          <SelectIntent
            dialogId={dialogId}
            languages={languages}
            luFeatures={luFeatures}
            manifest={skillManifest}
            manifestDirPath={manifestDirPath}
            projectId={projectId}
            rootLuFiles={luFiles}
            runtime={runtime}
            zipContent={zipContent}
            onBack={() => {
              setTitle({