@backstage/plugin-catalog-import 0.7.2 → 0.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # @backstage/plugin-catalog-import
2
2
 
3
+ ## 0.7.6
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/core-plugin-api@0.4.0
9
+ - @backstage/plugin-catalog-react@0.6.8
10
+ - @backstage/core-components@0.8.2
11
+ - @backstage/catalog-client@0.5.3
12
+ - @backstage/integration-react@0.1.16
13
+
14
+ ## 0.7.5
15
+
16
+ ### Patch Changes
17
+
18
+ - cd450844f6: Moved React dependencies to `peerDependencies` and allow both React v16 and v17 to be used.
19
+ - Updated dependencies
20
+ - @backstage/core-components@0.8.0
21
+ - @backstage/core-plugin-api@0.3.0
22
+ - @backstage/integration-react@0.1.15
23
+ - @backstage/plugin-catalog-react@0.6.5
24
+
25
+ ## 0.7.4
26
+
27
+ ### Patch Changes
28
+
29
+ - a125278b81: Refactor out the deprecated path and icon from RouteRefs
30
+ - Updated dependencies
31
+ - @backstage/catalog-client@0.5.2
32
+ - @backstage/catalog-model@0.9.7
33
+ - @backstage/plugin-catalog-react@0.6.4
34
+ - @backstage/core-components@0.7.4
35
+ - @backstage/core-plugin-api@0.2.0
36
+ - @backstage/integration-react@0.1.14
37
+
38
+ ## 0.7.3
39
+
40
+ ### Patch Changes
41
+
42
+ - 36e67d2f24: Internal updates to apply more strict checks to throw errors.
43
+ - Updated dependencies
44
+ - @backstage/core-components@0.7.1
45
+ - @backstage/errors@0.1.3
46
+ - @backstage/core-plugin-api@0.1.11
47
+ - @backstage/plugin-catalog-react@0.6.1
48
+ - @backstage/catalog-model@0.9.5
49
+
3
50
  ## 0.7.2
4
51
 
5
52
  ### Patch Changes
@@ -19,6 +19,7 @@ import 'lodash/partition';
19
19
  import 'react-hook-form';
20
20
  import '@material-ui/lab';
21
21
  import 'yaml';
22
+ import '@backstage/errors';
22
23
  import 'react-use';
23
24
  import '@backstage/catalog-model';
24
25
 
@@ -28,4 +29,4 @@ const ImportPage = () => {
28
29
  };
29
30
 
30
31
  export { ImportPage };
31
- //# sourceMappingURL=index-35fd3502.esm.js.map
32
+ //# sourceMappingURL=index-40fc248d.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-35fd3502.esm.js","sources":["../../src/components/ImportPage/ImportPage.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport { useOutlet } from 'react-router';\nimport { DefaultImportPage } from '../DefaultImportPage';\n\nexport const ImportPage = () => {\n const outlet = useOutlet();\n\n return outlet || <DefaultImportPage />;\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;MAoBa,aAAa,MAAM;AAC9B,QAAM,SAAS;AAEf,SAAO,8CAAW,mBAAD;AAAA;;;;"}
1
+ {"version":3,"file":"index-40fc248d.esm.js","sources":["../../src/components/ImportPage/ImportPage.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport { useOutlet } from 'react-router';\nimport { DefaultImportPage } from '../DefaultImportPage';\n\nexport const ImportPage = () => {\n const outlet = useOutlet();\n\n return outlet || <DefaultImportPage />;\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;MAoBa,aAAa,MAAM;AAC9B,QAAM,SAAS;AAEf,SAAO,8CAAW,mBAAD;AAAA;;;;"}
package/dist/index.esm.js CHANGED
@@ -17,12 +17,12 @@ import partition from 'lodash/partition';
17
17
  import { useForm, Controller, FormProvider } from 'react-hook-form';
18
18
  import { Autocomplete } from '@material-ui/lab';
19
19
  import YAML from 'yaml';
20
+ import { assertError } from '@backstage/errors';
20
21
  import { useAsync } from 'react-use';
21
22
  import { stringifyEntityRef } from '@backstage/catalog-model';
22
23
 
23
24
  const catalogImportApiRef = createApiRef({
24
- id: "plugin.catalog-import.service",
25
- description: "Used by the catalog import plugin to make requests"
25
+ id: "plugin.catalog-import.service"
26
26
  });
27
27
 
28
28
  const getGithubIntegrationConfig = (scmIntegrationsApi, location) => {
@@ -30,7 +30,7 @@ const getGithubIntegrationConfig = (scmIntegrationsApi, location) => {
30
30
  if (!integration) {
31
31
  return void 0;
32
32
  }
33
- const {name: repo, owner} = parseGitUrl(location);
33
+ const { name: repo, owner } = parseGitUrl(location);
34
34
  return {
35
35
  repo,
36
36
  owner,
@@ -138,11 +138,11 @@ For more information, read an [overview of the Backstage software catalog](https
138
138
  const response = await fetch(`${await this.discoveryApi.getBaseUrl("catalog")}/analyze-location`, {
139
139
  headers: {
140
140
  "Content-Type": "application/json",
141
- ...idToken && {Authorization: `Bearer ${idToken}`}
141
+ ...idToken && { Authorization: `Bearer ${idToken}` }
142
142
  },
143
143
  method: "POST",
144
144
  body: JSON.stringify({
145
- location: {type: "url", target: repo}
145
+ location: { type: "url", target: repo }
146
146
  })
147
147
  }).catch((e) => {
148
148
  throw new Error(`Failed to generate entity definitions, ${e.message}`);
@@ -159,19 +159,19 @@ For more information, read an [overview of the Backstage software catalog](https
159
159
  repo,
160
160
  githubIntegrationConfig
161
161
  }) {
162
- const {token} = await this.scmAuthApi.getCredentials({url});
162
+ const { token } = await this.scmAuthApi.getCredentials({ url });
163
163
  const octo = new Octokit({
164
164
  auth: token,
165
165
  baseUrl: githubIntegrationConfig.apiBaseUrl
166
166
  });
167
167
  const catalogFileName = "catalog-info.yaml";
168
168
  const query = `repo:${owner}/${repo}+filename:${catalogFileName}`;
169
- const searchResult = await octo.search.code({q: query}).catch((e) => {
169
+ const searchResult = await octo.search.code({ q: query }).catch((e) => {
170
170
  throw new Error(formatHttpErrorMessage("Couldn't search repository for metadata file.", e));
171
171
  });
172
172
  const exists = searchResult.data.total_count > 0;
173
173
  if (exists) {
174
- const repoInformation = await octo.repos.get({owner, repo}).catch((e) => {
174
+ const repoInformation = await octo.repos.get({ owner, repo }).catch((e) => {
175
175
  throw new Error(formatHttpErrorMessage("Couldn't fetch repo data", e));
176
176
  });
177
177
  const defaultBranch = repoInformation.data.default_branch;
@@ -206,7 +206,7 @@ For more information, read an [overview of the Backstage software catalog](https
206
206
  repositoryUrl,
207
207
  githubIntegrationConfig
208
208
  }) {
209
- const {token} = await this.scmAuthApi.getCredentials({
209
+ const { token } = await this.scmAuthApi.getCredentials({
210
210
  url: repositoryUrl,
211
211
  additionalScope: {
212
212
  repoWrite: true
@@ -270,8 +270,7 @@ function formatHttpErrorMessage(message, error) {
270
270
  }
271
271
 
272
272
  const rootRouteRef = createRouteRef({
273
- path: "",
274
- title: "catalog-import"
273
+ id: "catalog-import"
275
274
  });
276
275
  const catalogImportPlugin = createPlugin({
277
276
  id: "catalog-import",
@@ -309,7 +308,7 @@ const catalogImportPlugin = createPlugin({
309
308
  });
310
309
  const CatalogImportPage = catalogImportPlugin.provide(createRoutableExtension({
311
310
  name: "CatalogImportPage",
312
- component: () => import('./esm/index-35fd3502.esm.js').then((m) => m.ImportPage),
311
+ component: () => import('./esm/index-40fc248d.esm.js').then((m) => m.ImportPage),
313
312
  mountPoint: rootRouteRef
314
313
  }));
315
314
 
@@ -370,7 +369,7 @@ function reducer(state, action) {
370
369
  if (state.activeState !== "analyze") {
371
370
  return state;
372
371
  }
373
- const {activeState, previousStates} = state;
372
+ const { activeState, previousStates } = state;
374
373
  const [activeFlow, analysisUrl, analyzeResult, opts] = action.args;
375
374
  return {
376
375
  ...state,
@@ -386,7 +385,7 @@ function reducer(state, action) {
386
385
  if (state.activeState !== "prepare") {
387
386
  return state;
388
387
  }
389
- const {activeState, previousStates} = state;
388
+ const { activeState, previousStates } = state;
390
389
  const [prepareResult, opts] = action.args;
391
390
  return {
392
391
  ...state,
@@ -399,7 +398,7 @@ function reducer(state, action) {
399
398
  if (state.activeState !== "review") {
400
399
  return state;
401
400
  }
402
- const {activeState, previousStates} = state;
401
+ const { activeState, previousStates } = state;
403
402
  const [reviewResult] = action.args;
404
403
  return {
405
404
  ...state,
@@ -409,7 +408,7 @@ function reducer(state, action) {
409
408
  };
410
409
  }
411
410
  case "onGoBack": {
412
- const {activeState, previousStates} = state;
411
+ const { activeState, previousStates } = state;
413
412
  return {
414
413
  ...state,
415
414
  activeState: previousStates.length > 0 ? previousStates[previousStates.length - 1] : activeState,
@@ -427,7 +426,7 @@ function reducer(state, action) {
427
426
  }
428
427
  const useImportState = (options) => {
429
428
  const [state, dispatch] = useReducer(reducer, options == null ? void 0 : options.initialUrl, init);
430
- const {activeFlow, activeState, analysisUrl, previousStates} = state;
429
+ const { activeFlow, activeState, analysisUrl, previousStates } = state;
431
430
  return {
432
431
  activeFlow,
433
432
  activeState,
@@ -444,9 +443,9 @@ const useImportState = (options) => {
444
443
  type: "onPrepare",
445
444
  args: [result, opts]
446
445
  }),
447
- onReview: (result) => dispatch({type: "onReview", args: [result]}),
448
- onGoBack: previousStates.length > 0 ? () => dispatch({type: "onGoBack"}) : void 0,
449
- onReset: () => dispatch({type: "onReset", initialUrl: options == null ? void 0 : options.initialUrl})
446
+ onReview: (result) => dispatch({ type: "onReview", args: [result] }),
447
+ onGoBack: previousStates.length > 0 ? () => dispatch({ type: "onGoBack" }) : void 0,
448
+ onReset: () => dispatch({ type: "onReset", initialUrl: options == null ? void 0 : options.initialUrl })
450
449
  };
451
450
  };
452
451
 
@@ -469,7 +468,7 @@ const useStyles$3 = makeStyles((theme) => ({
469
468
  }
470
469
  }));
471
470
  const NextButton = (props) => {
472
- const {loading, ...buttonProps} = props;
471
+ const { loading, ...buttonProps } = props;
473
472
  const classes = useStyles$3();
474
473
  return /* @__PURE__ */ React.createElement("div", {
475
474
  className: classes.wrapper
@@ -493,7 +492,7 @@ const BackButton = (props) => {
493
492
  };
494
493
 
495
494
  function asInputRef(renderResult) {
496
- const {ref, ...rest} = renderResult;
495
+ const { ref, ...rest } = renderResult;
497
496
  return {
498
497
  inputRef: ref,
499
498
  ...rest
@@ -559,7 +558,7 @@ const EntityListComponent = ({
559
558
  }))))));
560
559
  };
561
560
 
562
- const StepFinishImportLocation = ({prepareResult, onReset}) => {
561
+ const StepFinishImportLocation = ({ prepareResult, onReset }) => {
563
562
  const continueButton = /* @__PURE__ */ React.createElement(Grid, {
564
563
  container: true,
565
564
  spacing: 0
@@ -599,7 +598,7 @@ const StepInitAnalyzeUrl = ({
599
598
  const {
600
599
  register,
601
600
  handleSubmit,
602
- formState: {errors},
601
+ formState: { errors },
603
602
  watch
604
603
  } = useForm({
605
604
  mode: "onTouched",
@@ -609,7 +608,7 @@ const StepInitAnalyzeUrl = ({
609
608
  });
610
609
  const [submitted, setSubmitted] = useState(false);
611
610
  const [error, setError] = useState(void 0);
612
- const handleResult = useCallback(async ({url}) => {
611
+ const handleResult = useCallback(async ({ url }) => {
613
612
  var _a, _b, _c;
614
613
  setSubmitted(true);
615
614
  try {
@@ -645,7 +644,7 @@ const StepInitAnalyzeUrl = ({
645
644
  }
646
645
  }
647
646
  } catch (e) {
648
- setError((_c = (_b = (_a = e.data) == null ? void 0 : _a.error) == null ? void 0 : _b.message) != null ? _c : e.message);
647
+ setError((_c = (_b = (_a = e == null ? void 0 : e.data) == null ? void 0 : _a.error) == null ? void 0 : _b.message) != null ? _c : e.message);
649
648
  setSubmitted(false);
650
649
  }
651
650
  }, [catalogImportApi, disablePullRequest, errorApi, onAnalysis]);
@@ -696,7 +695,7 @@ const AutocompleteTextField = ({
696
695
  return /* @__PURE__ */ React.createElement(Controller, {
697
696
  name,
698
697
  rules,
699
- render: ({field: {onChange}}) => /* @__PURE__ */ React.createElement(Autocomplete, {
698
+ render: ({ field: { onChange } }) => /* @__PURE__ */ React.createElement(Autocomplete, {
700
699
  loading,
701
700
  loadingText,
702
701
  options: options || [],
@@ -728,13 +727,13 @@ const PreparePullRequestForm = ({
728
727
  onSubmit,
729
728
  render
730
729
  }) => {
731
- const methods = useForm({mode: "onTouched", defaultValues});
732
- const {handleSubmit, watch, control, register, formState, setValue} = methods;
730
+ const methods = useForm({ mode: "onTouched", defaultValues });
731
+ const { handleSubmit, watch, control, register, formState, setValue } = methods;
733
732
  return /* @__PURE__ */ React.createElement(FormProvider, {
734
733
  ...methods
735
734
  }, /* @__PURE__ */ React.createElement("form", {
736
735
  onSubmit: handleSubmit(onSubmit)
737
- }, render({values: watch(), formState, register, control, setValue})));
736
+ }, render({ values: watch(), formState, register, control, setValue })));
738
737
  };
739
738
 
740
739
  const PreviewCatalogInfoComponent = ({
@@ -792,7 +791,7 @@ function generateEntities(entities, componentName, owner) {
792
791
  },
793
792
  spec: {
794
793
  ...e.spec,
795
- ...owner ? {owner} : {}
794
+ ...owner ? { owner } : {}
796
795
  }
797
796
  }));
798
797
  }
@@ -819,11 +818,11 @@ const StepPrepareCreatePullRequest = ({
819
818
  errorApi.post(prDefaultsError);
820
819
  }
821
820
  }, [prDefaultsError, errorApi]);
822
- const {loading: groupsLoading, value: groups} = useAsync(async () => {
821
+ const { loading: groupsLoading, value: groups } = useAsync(async () => {
823
822
  const groupEntities = await catalogApi.getEntities({
824
- filter: {kind: "group"}
823
+ filter: { kind: "group" }
825
824
  });
826
- return groupEntities.items.map((e) => formatEntityRefTitle(e, {defaultKind: "group"})).sort();
825
+ return groupEntities.items.map((e) => formatEntityRefTitle(e, { defaultKind: "group" })).sort();
827
826
  });
828
827
  const handleResult = useCallback(async (data) => {
829
828
  setSubmitted(true);
@@ -851,8 +850,9 @@ const StepPrepareCreatePullRequest = ({
851
850
  }))
852
851
  }
853
852
  ]
854
- }, {notRepeatable: true});
853
+ }, { notRepeatable: true });
855
854
  } catch (e) {
855
+ assertError(e);
856
856
  setError(e.message);
857
857
  setSubmitted(false);
858
858
  }
@@ -872,7 +872,7 @@ const StepPrepareCreatePullRequest = ({
872
872
  componentName: ((_f = (_e = analyzeResult.generatedEntities[0]) == null ? void 0 : _e.metadata) == null ? void 0 : _f.name) || "",
873
873
  useCodeowners: false
874
874
  },
875
- render: ({values, formState, register, setValue}) => /* @__PURE__ */ React.createElement(React.Fragment, null, renderFormFields({
875
+ render: ({ values, formState, register, setValue }) => /* @__PURE__ */ React.createElement(React.Fragment, null, renderFormFields({
876
876
  values,
877
877
  formState,
878
878
  register,
@@ -997,7 +997,7 @@ const StepReviewLocation = ({
997
997
  var _a;
998
998
  const ref = stringifyEntityRef((_a = l.entities[0]) != null ? _a : l);
999
999
  await catalogApi.refreshEntity(ref);
1000
- return {target: l.target};
1000
+ return { target: l.target };
1001
1001
  }));
1002
1002
  }
1003
1003
  const locations = await Promise.all(prepareResult.locations.filter((l) => !l.exists).map(async (l) => {
@@ -1013,10 +1013,11 @@ const StepReviewLocation = ({
1013
1013
  }));
1014
1014
  onReview({
1015
1015
  ...prepareResult,
1016
- ...{refreshed},
1016
+ ...{ refreshed },
1017
1017
  locations
1018
1018
  });
1019
1019
  } catch (e) {
1020
+ assertError(e);
1020
1021
  if (prepareResult.type === "repository" && e.message.startsWith("Location was added but has no entities specified yet")) {
1021
1022
  onReview({
1022
1023
  ...prepareResult,
@@ -1145,7 +1146,7 @@ function defaultGenerateStepper(flow, defaults) {
1145
1146
  }, /* @__PURE__ */ React.createElement(Typography, {
1146
1147
  variant: "h6"
1147
1148
  }, "Entity Configuration")), /* @__PURE__ */ React.createElement(TextField, {
1148
- ...asInputRef(register("componentName", {required: true})),
1149
+ ...asInputRef(register("componentName", { required: true })),
1149
1150
  label: "Name of the created component",
1150
1151
  placeholder: "my-component",
1151
1152
  margin: "normal",
@@ -1165,7 +1166,7 @@ function defaultGenerateStepper(flow, defaults) {
1165
1166
  label: "Entity Owner",
1166
1167
  placeholder: "my-group"
1167
1168
  },
1168
- rules: {required: true},
1169
+ rules: { required: true },
1169
1170
  required: true
1170
1171
  }), /* @__PURE__ */ React.createElement(FormControlLabel, {
1171
1172
  control: /* @__PURE__ */ React.createElement(Checkbox, {
@@ -1187,7 +1188,7 @@ function defaultGenerateStepper(flow, defaults) {
1187
1188
  }
1188
1189
  }
1189
1190
  const defaultStepper = {
1190
- analyze: (state, {apis}) => ({
1191
+ analyze: (state, { apis }) => ({
1191
1192
  stepLabel: /* @__PURE__ */ React.createElement(StepLabel, null, "Select URL"),
1192
1193
  content: /* @__PURE__ */ React.createElement(StepInitAnalyzeUrl, {
1193
1194
  key: "analyze",
@@ -1235,7 +1236,7 @@ const ImportStepper = ({
1235
1236
  }) => {
1236
1237
  const catalogImportApi = useApi(catalogImportApiRef);
1237
1238
  const classes = useStyles();
1238
- const state = useImportState({initialUrl});
1239
+ const state = useImportState({ initialUrl });
1239
1240
  const states = useMemo(() => generateStepper(state.activeFlow, defaultStepper), [generateStepper, state.activeFlow]);
1240
1241
  const render = (step) => {
1241
1242
  return /* @__PURE__ */ React.createElement(Step, null, step.stepLabel, /* @__PURE__ */ React.createElement(StepContent, null, step.content));
@@ -1243,10 +1244,10 @@ const ImportStepper = ({
1243
1244
  return /* @__PURE__ */ React.createElement(InfoCard, {
1244
1245
  variant
1245
1246
  }, /* @__PURE__ */ React.createElement(Stepper, {
1246
- classes: {root: classes.stepperRoot},
1247
+ classes: { root: classes.stepperRoot },
1247
1248
  activeStep: state.activeStepNumber,
1248
1249
  orientation: "vertical"
1249
- }, render(states.analyze(state, {apis: {catalogImportApi}})), render(states.prepare(state, {apis: {catalogImportApi}})), render(states.review(state, {apis: {catalogImportApi}})), render(states.finish(state, {apis: {catalogImportApi}}))));
1250
+ }, render(states.analyze(state, { apis: { catalogImportApi } })), render(states.prepare(state, { apis: { catalogImportApi } })), render(states.review(state, { apis: { catalogImportApi } })), render(states.finish(state, { apis: { catalogImportApi } }))));
1250
1251
  };
1251
1252
 
1252
1253
  const DefaultImportPage = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/api/CatalogImportApi.ts","../src/api/GitHub.ts","../src/api/CatalogImportClient.ts","../src/plugin.ts","../src/components/ImportInfoCard/ImportInfoCard.tsx","../src/components/useImportState.ts","../src/components/Buttons/index.tsx","../src/components/helpers.ts","../src/components/EntityListComponent/EntityListComponent.tsx","../src/components/StepFinishImportLocation/StepFinishImportLocation.tsx","../src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.tsx","../src/components/StepPrepareCreatePullRequest/AutocompleteTextField.tsx","../src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.tsx","../src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.tsx","../src/components/StepPrepareCreatePullRequest/PreviewPullRequestComponent.tsx","../src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.tsx","../src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.tsx","../src/components/StepReviewLocation/StepReviewLocation.tsx","../src/components/ImportStepper/defaults.tsx","../src/components/ImportStepper/ImportStepper.tsx","../src/components/DefaultImportPage/DefaultImportPage.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { EntityName } from '@backstage/catalog-model';\nimport { createApiRef } from '@backstage/core-plugin-api';\nimport { PartialEntity } from '../types';\n\nexport const catalogImportApiRef = createApiRef<CatalogImportApi>({\n id: 'plugin.catalog-import.service',\n description: 'Used by the catalog import plugin to make requests',\n});\n\n// result of the analyze state\nexport type AnalyzeResult =\n | {\n type: 'locations';\n locations: Array<{\n target: string;\n exists?: boolean;\n entities: EntityName[];\n }>;\n }\n | {\n type: 'repository';\n url: string;\n integrationType: string;\n generatedEntities: PartialEntity[];\n };\n\nexport interface CatalogImportApi {\n analyzeUrl(url: string): Promise<AnalyzeResult>;\n\n preparePullRequest?(): Promise<{\n title: string;\n body: string;\n }>;\n submitPullRequest(options: {\n repositoryUrl: string;\n fileContent: string;\n title: string;\n body: string;\n }): Promise<{ link: string; location: string }>;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport parseGitUrl from 'git-url-parse';\n\nexport const getGithubIntegrationConfig = (\n scmIntegrationsApi: ScmIntegrationRegistry,\n location: string,\n) => {\n const integration = scmIntegrationsApi.github.byUrl(location);\n if (!integration) {\n return undefined;\n }\n\n const { name: repo, owner } = parseGitUrl(location);\n return {\n repo,\n owner,\n githubIntegrationConfig: integration.config,\n };\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { EntityName } from '@backstage/catalog-model';\nimport {\n ConfigApi,\n DiscoveryApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\nimport {\n GitHubIntegrationConfig,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport { ScmAuthApi } from '@backstage/integration-react';\nimport { Octokit } from '@octokit/rest';\nimport { Base64 } from 'js-base64';\nimport { PartialEntity } from '../types';\nimport { AnalyzeResult, CatalogImportApi } from './CatalogImportApi';\nimport { getGithubIntegrationConfig } from './GitHub';\nimport { trimEnd } from 'lodash';\n\nexport class CatalogImportClient implements CatalogImportApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly identityApi: IdentityApi;\n private readonly scmAuthApi: ScmAuthApi;\n private readonly scmIntegrationsApi: ScmIntegrationRegistry;\n private readonly catalogApi: CatalogApi;\n private readonly configApi: ConfigApi;\n\n constructor(options: {\n discoveryApi: DiscoveryApi;\n scmAuthApi: ScmAuthApi;\n identityApi: IdentityApi;\n scmIntegrationsApi: ScmIntegrationRegistry;\n catalogApi: CatalogApi;\n configApi: ConfigApi;\n }) {\n this.discoveryApi = options.discoveryApi;\n this.scmAuthApi = options.scmAuthApi;\n this.identityApi = options.identityApi;\n this.scmIntegrationsApi = options.scmIntegrationsApi;\n this.catalogApi = options.catalogApi;\n this.configApi = options.configApi;\n }\n\n async analyzeUrl(url: string): Promise<AnalyzeResult> {\n if (\n new URL(url).pathname.match(/\\.ya?ml$/) ||\n new URL(url).searchParams.get('path')?.match(/.ya?ml$/)\n ) {\n const location = await this.catalogApi.addLocation({\n type: 'url',\n target: url,\n dryRun: true,\n });\n\n return {\n type: 'locations',\n locations: [\n {\n exists: location.exists,\n target: location.location.target,\n entities: location.entities.map(e => ({\n kind: e.kind,\n namespace: e.metadata.namespace ?? 'default',\n name: e.metadata.name,\n })),\n },\n ],\n };\n }\n\n const ghConfig = getGithubIntegrationConfig(this.scmIntegrationsApi, url);\n if (!ghConfig) {\n const other = this.scmIntegrationsApi.byUrl(url);\n if (other) {\n throw new Error(\n `The ${other.title} integration only supports full URLs to catalog-info.yaml files. Did you try to pass in the URL of a directory instead?`,\n );\n }\n throw new Error(\n 'This URL was not recognized as a valid GitHub URL because there was no configured integration that matched the given host name. You could try to paste the full URL to a catalog-info.yaml file instead.',\n );\n }\n\n // TODO: this could be part of the analyze-location endpoint\n const locations = await this.checkGitHubForExistingCatalogInfo({\n ...ghConfig,\n url,\n });\n\n if (locations.length > 0) {\n return {\n type: 'locations',\n locations,\n };\n }\n\n return {\n type: 'repository',\n integrationType: 'github',\n url: url,\n generatedEntities: await this.generateEntityDefinitions({\n repo: url,\n }),\n };\n }\n\n async preparePullRequest(): Promise<{\n title: string;\n body: string;\n }> {\n const appTitle =\n this.configApi.getOptionalString('app.title') ?? 'Backstage';\n const appBaseUrl = this.configApi.getString('app.baseUrl');\n\n return {\n title: 'Add catalog-info.yaml config file',\n body: `This pull request adds a **Backstage entity metadata file** \\\nto this repository so that the component can be added to the \\\n[${appTitle} software catalog](${appBaseUrl}).\\n\\nAfter this pull request is merged, \\\nthe component will become available.\\n\\nFor more information, read an \\\n[overview of the Backstage software catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview).`,\n };\n }\n\n async submitPullRequest({\n repositoryUrl,\n fileContent,\n title,\n body,\n }: {\n repositoryUrl: string;\n fileContent: string;\n title: string;\n body: string;\n }): Promise<{ link: string; location: string }> {\n const ghConfig = getGithubIntegrationConfig(\n this.scmIntegrationsApi,\n repositoryUrl,\n );\n\n if (ghConfig) {\n return await this.submitGitHubPrToRepo({\n ...ghConfig,\n repositoryUrl,\n fileContent,\n title,\n body,\n });\n }\n\n throw new Error('unimplemented!');\n }\n\n // TODO: this could be part of the catalog api\n private async generateEntityDefinitions({\n repo,\n }: {\n repo: string;\n }): Promise<PartialEntity[]> {\n const idToken = await this.identityApi.getIdToken();\n const response = await fetch(\n `${await this.discoveryApi.getBaseUrl('catalog')}/analyze-location`,\n {\n headers: {\n 'Content-Type': 'application/json',\n ...(idToken && { Authorization: `Bearer ${idToken}` }),\n },\n method: 'POST',\n body: JSON.stringify({\n location: { type: 'url', target: repo },\n }),\n },\n ).catch(e => {\n throw new Error(`Failed to generate entity definitions, ${e.message}`);\n });\n if (!response.ok) {\n throw new Error(\n `Failed to generate entity definitions. Received http response ${response.status}: ${response.statusText}`,\n );\n }\n\n const payload = await response.json();\n return payload.generateEntities.map((x: any) => x.entity);\n }\n\n // TODO: this response should better be part of the analyze-locations response and scm-independent / implemented per scm\n private async checkGitHubForExistingCatalogInfo({\n url,\n owner,\n repo,\n githubIntegrationConfig,\n }: {\n url: string;\n owner: string;\n repo: string;\n githubIntegrationConfig: GitHubIntegrationConfig;\n }): Promise<\n Array<{\n target: string;\n entities: EntityName[];\n }>\n > {\n const { token } = await this.scmAuthApi.getCredentials({ url });\n const octo = new Octokit({\n auth: token,\n baseUrl: githubIntegrationConfig.apiBaseUrl,\n });\n const catalogFileName = 'catalog-info.yaml';\n const query = `repo:${owner}/${repo}+filename:${catalogFileName}`;\n\n const searchResult = await octo.search.code({ q: query }).catch(e => {\n throw new Error(\n formatHttpErrorMessage(\n \"Couldn't search repository for metadata file.\",\n e,\n ),\n );\n });\n const exists = searchResult.data.total_count > 0;\n if (exists) {\n const repoInformation = await octo.repos.get({ owner, repo }).catch(e => {\n throw new Error(formatHttpErrorMessage(\"Couldn't fetch repo data\", e));\n });\n const defaultBranch = repoInformation.data.default_branch;\n\n return await Promise.all(\n searchResult.data.items\n .map(i => `${trimEnd(url, '/')}/blob/${defaultBranch}/${i.path}`)\n .map(async target => {\n const result = await this.catalogApi.addLocation({\n type: 'url',\n target,\n dryRun: true,\n });\n return {\n target,\n exists: result.exists,\n entities: result.entities.map(e => ({\n kind: e.kind,\n namespace: e.metadata.namespace ?? 'default',\n name: e.metadata.name,\n })),\n };\n }),\n );\n }\n\n return [];\n }\n\n // TODO: extract this function and implement for non-github\n private async submitGitHubPrToRepo({\n owner,\n repo,\n title,\n body,\n fileContent,\n repositoryUrl,\n githubIntegrationConfig,\n }: {\n owner: string;\n repo: string;\n title: string;\n body: string;\n fileContent: string;\n repositoryUrl: string;\n githubIntegrationConfig: GitHubIntegrationConfig;\n }): Promise<{ link: string; location: string }> {\n const { token } = await this.scmAuthApi.getCredentials({\n url: repositoryUrl,\n additionalScope: {\n repoWrite: true,\n },\n });\n\n const octo = new Octokit({\n auth: token,\n baseUrl: githubIntegrationConfig.apiBaseUrl,\n });\n\n const branchName = 'backstage-integration';\n const fileName = 'catalog-info.yaml';\n\n const repoData = await octo.repos\n .get({\n owner,\n repo,\n })\n .catch(e => {\n throw new Error(formatHttpErrorMessage(\"Couldn't fetch repo data\", e));\n });\n\n const parentRef = await octo.git\n .getRef({\n owner,\n repo,\n ref: `heads/${repoData.data.default_branch}`,\n })\n .catch(e => {\n throw new Error(\n formatHttpErrorMessage(\"Couldn't fetch default branch data\", e),\n );\n });\n\n await octo.git\n .createRef({\n owner,\n repo,\n ref: `refs/heads/${branchName}`,\n sha: parentRef.data.object.sha,\n })\n .catch(e => {\n throw new Error(\n formatHttpErrorMessage(\n `Couldn't create a new branch with name '${branchName}'`,\n e,\n ),\n );\n });\n\n await octo.repos\n .createOrUpdateFileContents({\n owner,\n repo,\n path: fileName,\n message: title,\n content: Base64.encode(fileContent),\n branch: branchName,\n })\n .catch(e => {\n throw new Error(\n formatHttpErrorMessage(\n `Couldn't create a commit with ${fileName} file added`,\n e,\n ),\n );\n });\n\n const pullRequestResponse = await octo.pulls\n .create({\n owner,\n repo,\n title,\n head: branchName,\n body,\n base: repoData.data.default_branch,\n })\n .catch(e => {\n throw new Error(\n formatHttpErrorMessage(\n `Couldn't create a pull request for ${branchName} branch`,\n e,\n ),\n );\n });\n\n return {\n link: pullRequestResponse.data.html_url,\n location: `https://${githubIntegrationConfig.host}/${owner}/${repo}/blob/${repoData.data.default_branch}/${fileName}`,\n };\n }\n}\n\nfunction formatHttpErrorMessage(\n message: string,\n error: { status: number; message: string },\n) {\n return `${message}, received http response status code ${error.status}: ${error.message}`;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n configApiRef,\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n createRouteRef,\n discoveryApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\nimport {\n scmAuthApiRef,\n scmIntegrationsApiRef,\n} from '@backstage/integration-react';\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\nimport { catalogImportApiRef, CatalogImportClient } from './api';\n\nexport const rootRouteRef = createRouteRef({\n path: '',\n title: 'catalog-import',\n});\n\nexport const catalogImportPlugin = createPlugin({\n id: 'catalog-import',\n apis: [\n createApiFactory({\n api: catalogImportApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n scmAuthApi: scmAuthApiRef,\n identityApi: identityApiRef,\n scmIntegrationsApi: scmIntegrationsApiRef,\n catalogApi: catalogApiRef,\n configApi: configApiRef,\n },\n factory: ({\n discoveryApi,\n scmAuthApi,\n identityApi,\n scmIntegrationsApi,\n catalogApi,\n configApi,\n }) =>\n new CatalogImportClient({\n discoveryApi,\n scmAuthApi,\n scmIntegrationsApi,\n identityApi,\n catalogApi,\n configApi,\n }),\n }),\n ],\n routes: {\n importPage: rootRouteRef,\n },\n});\n\nexport const CatalogImportPage = catalogImportPlugin.provide(\n createRoutableExtension({\n name: 'CatalogImportPage',\n component: () => import('./components/ImportPage').then(m => m.ImportPage),\n mountPoint: rootRouteRef,\n }),\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InfoCard } from '@backstage/core-components';\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Chip, Typography } from '@material-ui/core';\nimport React from 'react';\nimport { catalogImportApiRef } from '../../api';\n\nexport const ImportInfoCard = () => {\n const configApi = useApi(configApiRef);\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n const catalogImportApi = useApi(catalogImportApiRef);\n\n const integrations = configApi.getConfig('integrations');\n const hasGithubIntegration = integrations.has('github');\n\n return (\n <InfoCard\n title=\"Register an existing component\"\n deepLink={{\n title: 'Learn more about the Software Catalog',\n link: 'https://backstage.io/docs/features/software-catalog/software-catalog-overview',\n }}\n >\n <Typography variant=\"body2\" paragraph>\n Enter the URL to your source code repository to add it to {appTitle}.\n </Typography>\n <Typography variant=\"h6\">Link to an existing entity file</Typography>\n <Typography variant=\"subtitle2\" color=\"textSecondary\" paragraph>\n Example:{' '}\n <code>\n https://github.com/backstage/backstage/blob/master/catalog-info.yaml\n </code>\n </Typography>\n <Typography variant=\"body2\" paragraph>\n The wizard analyzes the file, previews the entities, and adds them to\n the {appTitle} catalog.\n </Typography>\n {hasGithubIntegration && (\n <>\n <Typography variant=\"h6\">\n Link to a repository{' '}\n <Chip label=\"GitHub only\" variant=\"outlined\" size=\"small\" />\n </Typography>\n <Typography variant=\"subtitle2\" color=\"textSecondary\" paragraph>\n Example: <code>https://github.com/backstage/backstage</code>\n </Typography>\n <Typography variant=\"body2\" paragraph>\n The wizard discovers all <code>catalog-info.yaml</code> files in the\n repository, previews the entities, and adds them to the {appTitle}{' '}\n catalog.\n </Typography>\n {catalogImportApi.preparePullRequest && (\n <Typography variant=\"body2\" paragraph>\n If no entities are found, the wizard will prepare a Pull Request\n that adds an example <code>catalog-info.yaml</code> and prepares\n the {appTitle} catalog to load all entities as soon as the Pull\n Request is merged.\n </Typography>\n )}\n </>\n )}\n </InfoCard>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, EntityName } from '@backstage/catalog-model';\nimport { useReducer } from 'react';\nimport { AnalyzeResult } from '../api';\n\n// the configuration of the stepper\nexport type ImportFlows =\n | 'unknown'\n | 'single-location'\n | 'multiple-locations'\n | 'no-location';\n\n// the available states of the stepper\ntype ImportStateTypes = 'analyze' | 'prepare' | 'review' | 'finish';\n\n// result of the prepare state\nexport type PrepareResult =\n | {\n type: 'locations';\n locations: Array<{\n exists?: boolean;\n target: string;\n entities: EntityName[];\n }>;\n }\n | {\n type: 'repository';\n url: string;\n integrationType: string;\n pullRequest: {\n url: string;\n };\n locations: Array<{\n target: string;\n entities: EntityName[];\n }>;\n };\n\n// result of the review result\nexport type ReviewResult =\n | {\n type: 'locations';\n locations: Array<{\n target: string;\n entities: Entity[];\n }>;\n refreshed: Array<{ target: string }>;\n }\n | {\n type: 'repository';\n url: string;\n integrationType: string;\n pullRequest: {\n url: string;\n };\n locations: Array<{\n target: string;\n entities: Entity[];\n }>;\n };\n\n// function type for the 'analysis' -> 'prepare'/'review' transition\ntype onAnalysisFn = (\n flow: ImportFlows,\n url: string,\n result: AnalyzeResult,\n opts?: { prepareResult?: PrepareResult },\n) => void;\n\n// function type for the 'prepare' -> 'review' transition\ntype onPrepareFn = (\n result: PrepareResult,\n opts?: { notRepeatable?: boolean },\n) => void;\n\n// function type for the 'review' -> 'finish' transition\ntype onReviewFn = (result: ReviewResult) => void;\n\n// the type interfaces that are available in each state. every state provides\n// already known information and means to go to the next, or the previous step.\ntype State =\n | {\n activeState: 'analyze';\n onAnalysis: onAnalysisFn;\n }\n | {\n activeState: 'prepare';\n analyzeResult: AnalyzeResult;\n prepareResult?: PrepareResult;\n onPrepare: onPrepareFn;\n }\n | {\n activeState: 'review';\n analyzeResult: AnalyzeResult;\n prepareResult: PrepareResult;\n onReview: onReviewFn;\n }\n | {\n activeState: 'finish';\n analyzeResult: AnalyzeResult;\n prepareResult: PrepareResult;\n reviewResult: ReviewResult;\n };\n\nexport type ImportState = State & {\n activeFlow: ImportFlows;\n activeStepNumber: number;\n analysisUrl?: string;\n\n onGoBack?: () => void;\n onReset: () => void;\n};\n\ntype ReducerActions =\n | { type: 'onAnalysis'; args: Parameters<onAnalysisFn> }\n | { type: 'onPrepare'; args: Parameters<onPrepareFn> }\n | { type: 'onReview'; args: Parameters<onReviewFn> }\n | { type: 'onGoBack' }\n | { type: 'onReset'; initialUrl?: string };\n\ntype ReducerState = {\n activeFlow: ImportFlows;\n activeState: ImportStateTypes;\n analysisUrl?: string;\n analyzeResult?: AnalyzeResult;\n prepareResult?: PrepareResult;\n reviewResult?: ReviewResult;\n\n previousStates: ImportStateTypes[];\n};\n\nfunction init(initialUrl?: string): ReducerState {\n return {\n activeFlow: 'unknown',\n activeState: 'analyze',\n analysisUrl: initialUrl,\n previousStates: [],\n };\n}\n\nfunction reducer(state: ReducerState, action: ReducerActions): ReducerState {\n switch (action.type) {\n case 'onAnalysis': {\n if (state.activeState !== 'analyze') {\n return state;\n }\n\n const { activeState, previousStates } = state;\n const [activeFlow, analysisUrl, analyzeResult, opts] = action.args;\n\n return {\n ...state,\n analysisUrl,\n activeFlow,\n analyzeResult,\n prepareResult: opts?.prepareResult,\n\n activeState: opts?.prepareResult === undefined ? 'prepare' : 'review',\n previousStates: previousStates.concat(activeState),\n };\n }\n\n case 'onPrepare': {\n if (state.activeState !== 'prepare') {\n return state;\n }\n\n const { activeState, previousStates } = state;\n const [prepareResult, opts] = action.args;\n\n return {\n ...state,\n prepareResult,\n\n activeState: 'review',\n previousStates: opts?.notRepeatable\n ? []\n : previousStates.concat(activeState),\n };\n }\n\n case 'onReview': {\n if (state.activeState !== 'review') {\n return state;\n }\n\n const { activeState, previousStates } = state;\n const [reviewResult] = action.args;\n\n return {\n ...state,\n reviewResult,\n\n activeState: 'finish',\n previousStates: previousStates.concat(activeState),\n };\n }\n\n case 'onGoBack': {\n const { activeState, previousStates } = state;\n\n return {\n ...state,\n\n activeState:\n previousStates.length > 0\n ? previousStates[previousStates.length - 1]\n : activeState,\n previousStates: previousStates.slice(0, previousStates.length - 1),\n };\n }\n\n case 'onReset':\n return {\n ...init(action.initialUrl),\n\n // we keep the old prepareResult since the form is animated and an\n // undefined value might crash the last step.\n prepareResult: state.prepareResult,\n };\n\n default:\n throw new Error();\n }\n}\n\n/**\n * A hook that manages the state machine of the form. It handles different flows\n * which each can implement up to four states:\n * 1. analyze\n * 2. preview (skippable from analyze->review)\n * 3. review\n * 4. finish\n *\n * @param options options\n */\nexport const useImportState = (options?: {\n initialUrl?: string;\n}): ImportState => {\n const [state, dispatch] = useReducer(reducer, options?.initialUrl, init);\n\n const { activeFlow, activeState, analysisUrl, previousStates } = state;\n\n return {\n activeFlow,\n activeState,\n activeStepNumber: ['analyze', 'prepare', 'review', 'finish'].indexOf(\n activeState,\n ),\n analysisUrl: analysisUrl,\n\n analyzeResult: state.analyzeResult!,\n prepareResult: state.prepareResult!,\n reviewResult: state.reviewResult!,\n\n onAnalysis: (flow, url, result, opts) =>\n dispatch({\n type: 'onAnalysis',\n args: [flow, url, result, opts],\n }),\n\n onPrepare: (result, opts) =>\n dispatch({\n type: 'onPrepare',\n args: [result, opts],\n }),\n\n onReview: result => dispatch({ type: 'onReview', args: [result] }),\n\n onGoBack:\n previousStates.length > 0\n ? () => dispatch({ type: 'onGoBack' })\n : undefined,\n\n onReset: () =>\n dispatch({ type: 'onReset', initialUrl: options?.initialUrl }),\n };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Button, CircularProgress } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport React, { ComponentProps } from 'react';\n\nconst useStyles = makeStyles(theme => ({\n wrapper: {\n marginTop: theme.spacing(1),\n marginRight: theme.spacing(1),\n position: 'relative',\n },\n buttonProgress: {\n position: 'absolute',\n top: '50%',\n left: '50%',\n marginTop: -12,\n marginLeft: -12,\n },\n button: {\n marginTop: theme.spacing(1),\n marginRight: theme.spacing(1),\n },\n}));\n\nexport const NextButton = (\n props: ComponentProps<typeof Button> & { loading?: boolean },\n) => {\n const { loading, ...buttonProps } = props;\n const classes = useStyles();\n\n return (\n <div className={classes.wrapper}>\n <Button\n color=\"primary\"\n variant=\"contained\"\n {...buttonProps}\n disabled={props.disabled || props.loading}\n />\n {props.loading && (\n <CircularProgress size=\"1.5rem\" className={classes.buttonProgress} />\n )}\n {props.loading}\n </div>\n );\n};\n\nexport const BackButton = (props: ComponentProps<typeof Button>) => {\n const classes = useStyles();\n\n return (\n <Button variant=\"outlined\" className={classes.button} {...props}>\n {props.children || 'Back'}\n </Button>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { UseFormRegisterReturn } from 'react-hook-form';\n\n/**\n * A helper that converts the result of a render('name', opts) to make it compatible with material-ui.\n *\n * See also https://github.com/react-hook-form/react-hook-form/issues/4629#issuecomment-815840872\n * TODO: remove when updating to material-ui v5 (https://github.com/mui-org/material-ui/pull/23174)\n *\n * @param renderResult - the result of a render('name', opts)\n */\nexport function asInputRef(renderResult: UseFormRegisterReturn) {\n const { ref, ...rest } = renderResult;\n return {\n inputRef: ref,\n ...rest,\n };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, EntityName } from '@backstage/catalog-model';\nimport { useApp } from '@backstage/core-plugin-api';\nimport {\n EntityRefLink,\n formatEntityRefTitle,\n} from '@backstage/plugin-catalog-react';\nimport {\n Collapse,\n IconButton,\n List,\n ListItem,\n ListItemIcon,\n ListItemSecondaryAction,\n ListItemText,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport ExpandLessIcon from '@material-ui/icons/ExpandLess';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport WorkIcon from '@material-ui/icons/Work';\nimport React, { useState } from 'react';\n\nconst useStyles = makeStyles(theme => ({\n nested: {\n paddingLeft: theme.spacing(4),\n },\n}));\n\nfunction sortEntities(entities: Array<EntityName | Entity>) {\n return entities.sort((a, b) =>\n formatEntityRefTitle(a).localeCompare(formatEntityRefTitle(b)),\n );\n}\n\ntype Props = {\n locations: Array<{ target: string; entities: (Entity | EntityName)[] }>;\n locationListItemIcon: (target: string) => React.ReactElement;\n collapsed?: boolean;\n firstListItem?: React.ReactElement;\n onItemClick?: (target: string) => void;\n withLinks?: boolean;\n};\n\nexport const EntityListComponent = ({\n locations,\n collapsed = false,\n locationListItemIcon,\n onItemClick,\n firstListItem,\n withLinks = false,\n}: Props) => {\n const app = useApp();\n const classes = useStyles();\n\n const [expandedUrls, setExpandedUrls] = useState<string[]>([]);\n\n const handleClick = (url: string) => {\n setExpandedUrls(urls =>\n urls.includes(url) ? urls.filter(u => u !== url) : urls.concat(url),\n );\n };\n\n return (\n <List>\n {firstListItem}\n {locations.map(r => (\n <React.Fragment key={r.target}>\n <ListItem\n dense\n button={Boolean(onItemClick) as any}\n onClick={() => onItemClick?.call(this, r.target)}\n >\n <ListItemIcon>{locationListItemIcon(r.target)}</ListItemIcon>\n\n <ListItemText\n primary={r.target}\n secondary={`Entities: ${r.entities.length}`}\n />\n\n {collapsed && (\n <ListItemSecondaryAction>\n <IconButton edge=\"end\" onClick={() => handleClick(r.target)}>\n {expandedUrls.includes(r.target) ? (\n <ExpandLessIcon />\n ) : (\n <ExpandMoreIcon />\n )}\n </IconButton>\n </ListItemSecondaryAction>\n )}\n </ListItem>\n\n <Collapse\n in={!collapsed || expandedUrls.includes(r.target)}\n timeout=\"auto\"\n unmountOnExit\n >\n <List component=\"div\" disablePadding dense>\n {sortEntities(r.entities).map(entity => {\n const Icon =\n app.getSystemIcon(\n `kind:${entity.kind.toLocaleLowerCase('en-US')}`,\n ) ?? WorkIcon;\n return (\n <ListItem\n key={formatEntityRefTitle(entity)}\n className={classes.nested}\n {...(withLinks\n ? {\n component: EntityRefLink,\n entityRef: entity,\n button: withLinks as any,\n }\n : {})}\n >\n <ListItemIcon>\n <Icon />\n </ListItemIcon>\n <ListItemText primary={formatEntityRefTitle(entity)} />\n </ListItem>\n );\n })}\n </List>\n </Collapse>\n </React.Fragment>\n ))}\n </List>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Grid, Typography } from '@material-ui/core';\nimport LocationOnIcon from '@material-ui/icons/LocationOn';\nimport React from 'react';\nimport { BackButton } from '../Buttons';\nimport { EntityListComponent } from '../EntityListComponent';\nimport { PrepareResult } from '../useImportState';\nimport { Link } from '@backstage/core-components';\nimport partition from 'lodash/partition';\n\ntype Props = {\n prepareResult: PrepareResult;\n onReset: () => void;\n};\n\nexport const StepFinishImportLocation = ({ prepareResult, onReset }: Props) => {\n const continueButton = (\n <Grid container spacing={0}>\n <BackButton onClick={onReset}>Register another</BackButton>\n </Grid>\n );\n\n if (prepareResult.type === 'repository') {\n return (\n <>\n <Typography paragraph>\n The following Pull Request has been opened:{' '}\n <Link\n to={prepareResult.pullRequest.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {prepareResult.pullRequest.url}\n </Link>\n </Typography>\n\n <Typography paragraph>\n Your entities will be imported as soon as the Pull Request is merged.\n </Typography>\n\n {continueButton}\n </>\n );\n }\n\n const [existingLocations, newLocations] = partition(\n prepareResult.locations,\n l => l.exists,\n );\n\n return (\n <>\n {newLocations.length > 0 && (\n <>\n <Typography>\n The following entities have been added to the catalog:\n </Typography>\n\n <EntityListComponent\n locations={newLocations}\n locationListItemIcon={() => <LocationOnIcon />}\n withLinks\n />\n </>\n )}\n {existingLocations.length > 0 && (\n <>\n <Typography>\n A refresh was triggered for the following locations:\n </Typography>\n\n <EntityListComponent\n locations={existingLocations}\n locationListItemIcon={() => <LocationOnIcon />}\n withLinks\n />\n </>\n )}\n {continueButton}\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport { FormHelperText, Grid, TextField } from '@material-ui/core';\nimport React, { useCallback, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { AnalyzeResult, catalogImportApiRef } from '../../api';\nimport { NextButton } from '../Buttons';\nimport { asInputRef } from '../helpers';\nimport { ImportFlows, PrepareResult } from '../useImportState';\n\ntype FormData = {\n url: string;\n};\n\ntype Props = {\n onAnalysis: (\n flow: ImportFlows,\n url: string,\n result: AnalyzeResult,\n opts?: { prepareResult?: PrepareResult },\n ) => void;\n disablePullRequest?: boolean;\n analysisUrl?: string;\n};\n\n/**\n * A form that lets the user input a url and analyze it for existing locations or potential entities.\n *\n * @param onAnalysis is called when the analysis was successful\n * @param analysisUrl a url that can be used as a default value\n * @param disablePullRequest if true, repositories without entities will abort the wizard\n */\nexport const StepInitAnalyzeUrl = ({\n onAnalysis,\n analysisUrl = '',\n disablePullRequest = false,\n}: Props) => {\n const errorApi = useApi(errorApiRef);\n const catalogImportApi = useApi(catalogImportApiRef);\n\n const {\n register,\n handleSubmit,\n formState: { errors },\n watch,\n } = useForm<FormData>({\n mode: 'onTouched',\n defaultValues: {\n url: analysisUrl,\n },\n });\n\n const [submitted, setSubmitted] = useState(false);\n const [error, setError] = useState<string | undefined>(undefined);\n\n const handleResult = useCallback(\n async ({ url }: FormData) => {\n setSubmitted(true);\n\n try {\n const analysisResult = await catalogImportApi.analyzeUrl(url);\n\n switch (analysisResult.type) {\n case 'repository':\n if (\n !disablePullRequest &&\n analysisResult.generatedEntities.length > 0\n ) {\n onAnalysis('no-location', url, analysisResult);\n } else {\n setError(\"Couldn't generate entities for your repository\");\n setSubmitted(false);\n }\n break;\n\n case 'locations': {\n if (analysisResult.locations.length === 1) {\n onAnalysis('single-location', url, analysisResult, {\n prepareResult: analysisResult,\n });\n } else if (analysisResult.locations.length > 1) {\n onAnalysis('multiple-locations', url, analysisResult);\n } else {\n setError('There are no entities at this location');\n setSubmitted(false);\n }\n break;\n }\n\n default: {\n const err = `Received unknown analysis result of type ${\n (analysisResult as any).type\n }. Please contact the support team.`;\n setError(err);\n setSubmitted(false);\n\n errorApi.post(new Error(err));\n break;\n }\n }\n } catch (e) {\n setError(e.data?.error?.message ?? e.message);\n setSubmitted(false);\n }\n },\n [catalogImportApi, disablePullRequest, errorApi, onAnalysis],\n );\n\n return (\n <form onSubmit={handleSubmit(handleResult)}>\n <TextField\n {...asInputRef(\n register('url', {\n required: true,\n validate: {\n httpsValidator: (value: any) =>\n (typeof value === 'string' &&\n value.match(/^http[s]?:\\/\\//) !== null) ||\n 'Must start with http:// or https://.',\n },\n }),\n )}\n fullWidth\n id=\"url\"\n label=\"Repository URL\"\n placeholder=\"https://github.com/backstage/backstage/blob/master/catalog-info.yaml\"\n helperText=\"Enter the full path to your entity file to start tracking your component\"\n margin=\"normal\"\n variant=\"outlined\"\n error={Boolean(errors.url)}\n required\n />\n\n {errors.url && (\n <FormHelperText error>{errors.url.message}</FormHelperText>\n )}\n\n {error && <FormHelperText error>{error}</FormHelperText>}\n\n <Grid container spacing={0}>\n <NextButton\n disabled={Boolean(errors.url) || !watch('url')}\n loading={submitted}\n type=\"submit\"\n >\n Analyze\n </NextButton>\n </Grid>\n </form>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CircularProgress, TextField } from '@material-ui/core';\nimport { TextFieldProps } from '@material-ui/core/TextField/TextField';\nimport { Autocomplete } from '@material-ui/lab';\nimport React from 'react';\nimport { Controller, FieldErrors } from 'react-hook-form';\n\ntype Props<TFieldValue extends string> = {\n name: TFieldValue;\n options: string[];\n required?: boolean;\n\n errors?: FieldErrors;\n rules?: React.ComponentProps<typeof Controller>['rules'];\n\n loading?: boolean;\n loadingText?: string;\n\n helperText?: React.ReactNode;\n errorHelperText?: string;\n\n textFieldProps?: Omit<TextFieldProps, 'required' | 'fullWidth'>;\n};\n\nexport const AutocompleteTextField = <TFieldValue extends string>({\n name,\n options,\n required,\n errors,\n rules,\n loading = false,\n loadingText,\n helperText,\n errorHelperText,\n textFieldProps = {},\n}: Props<TFieldValue>) => {\n return (\n <Controller\n name={name}\n rules={rules}\n render={({ field: { onChange } }) => (\n <Autocomplete\n loading={loading}\n loadingText={loadingText}\n options={options || []}\n autoSelect\n freeSolo\n onChange={(_event: React.ChangeEvent<{}>, value: string | null) =>\n onChange(value)\n }\n renderInput={params => (\n <TextField\n {...params}\n helperText={(errors?.[name] && errorHelperText) || helperText}\n error={Boolean(errors?.[name])}\n margin=\"normal\"\n variant=\"outlined\"\n required={required}\n InputProps={{\n ...params.InputProps,\n endAdornment: (\n <React.Fragment>\n {loading ? (\n <CircularProgress color=\"inherit\" size=\"1em\" />\n ) : null}\n {params.InputProps.endAdornment}\n </React.Fragment>\n ),\n }}\n {...textFieldProps}\n />\n )}\n />\n )}\n />\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport {\n FormProvider,\n SubmitHandler,\n UnpackNestedValue,\n useForm,\n UseFormProps,\n UseFormReturn,\n} from 'react-hook-form';\n\ntype Props<TFieldValues extends Record<string, any>> = Pick<\n UseFormProps<TFieldValues>,\n 'defaultValues'\n> & {\n onSubmit: SubmitHandler<TFieldValues>;\n\n render: (\n props: Pick<\n UseFormReturn<TFieldValues>,\n 'formState' | 'register' | 'control' | 'setValue'\n > & {\n values: UnpackNestedValue<TFieldValues>;\n },\n ) => React.ReactNode;\n};\n\n/**\n * A form wrapper that creates a form that is used to prepare a pull request. It\n * hosts the form logic.\n *\n * @param defaultValues the default values of the form\n * @param onSubmit a callback that is executed when the form is submitted\n * (initiated by a button of type=\"submit\")\n * @param render render the form elements\n */\nexport const PreparePullRequestForm = <\n TFieldValues extends Record<string, any>,\n>({\n defaultValues,\n onSubmit,\n render,\n}: Props<TFieldValues>) => {\n const methods = useForm<TFieldValues>({ mode: 'onTouched', defaultValues });\n const { handleSubmit, watch, control, register, formState, setValue } =\n methods;\n\n return (\n <FormProvider {...methods}>\n <form onSubmit={handleSubmit(onSubmit)}>\n {render({ values: watch(), formState, register, control, setValue })}\n </form>\n </FormProvider>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { Card, CardContent, CardHeader } from '@material-ui/core';\nimport React from 'react';\nimport YAML from 'yaml';\nimport { CodeSnippet } from '@backstage/core-components';\nimport { trimEnd } from 'lodash';\n\ntype Props = {\n repositoryUrl: string;\n entities: Entity[];\n classes?: { card?: string; cardContent?: string };\n};\n\nexport const PreviewCatalogInfoComponent = ({\n repositoryUrl,\n entities,\n classes,\n}: Props) => {\n return (\n <Card variant=\"outlined\" className={classes?.card}>\n <CardHeader\n title={\n <code>{`${trimEnd(repositoryUrl, '/')}/catalog-info.yaml`}</code>\n }\n />\n\n <CardContent className={classes?.cardContent}>\n <CodeSnippet\n text={entities\n .map(e => YAML.stringify(e))\n .join('---\\n')\n .trim()}\n language=\"yaml\"\n />\n </CardContent>\n </Card>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Card, CardContent, CardHeader } from '@material-ui/core';\nimport React from 'react';\nimport { MarkdownContent } from '@backstage/core-components';\n\ntype Props = {\n title: string;\n description: string;\n classes?: { card?: string; cardContent?: string };\n};\n\nexport const PreviewPullRequestComponent = ({\n title,\n description,\n classes,\n}: Props) => {\n return (\n <Card variant=\"outlined\" className={classes?.card}>\n <CardHeader title={title} subheader=\"Create a new Pull Request\" />\n <CardContent className={classes?.cardContent}>\n <MarkdownContent content={description} />\n </CardContent>\n </Card>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n catalogApiRef,\n formatEntityRefTitle,\n} from '@backstage/plugin-catalog-react';\nimport { Box, FormHelperText, Grid, Typography } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport React, { useCallback, useEffect, useState } from 'react';\nimport { UnpackNestedValue, UseFormReturn } from 'react-hook-form';\nimport { useAsync } from 'react-use';\nimport YAML from 'yaml';\nimport { AnalyzeResult, catalogImportApiRef } from '../../api';\nimport { PartialEntity } from '../../types';\nimport { BackButton, NextButton } from '../Buttons';\nimport { PrepareResult } from '../useImportState';\nimport { PreparePullRequestForm } from './PreparePullRequestForm';\nimport { PreviewCatalogInfoComponent } from './PreviewCatalogInfoComponent';\nimport { PreviewPullRequestComponent } from './PreviewPullRequestComponent';\n\nconst useStyles = makeStyles(theme => ({\n previewCard: {\n marginTop: theme.spacing(1),\n },\n previewCardContent: {\n paddingTop: 0,\n },\n}));\n\ntype FormData = {\n title: string;\n body: string;\n componentName: string;\n owner: string;\n useCodeowners: boolean;\n};\n\ntype Props = {\n analyzeResult: Extract<AnalyzeResult, { type: 'repository' }>;\n onPrepare: (\n result: PrepareResult,\n opts?: { notRepeatable?: boolean },\n ) => void;\n onGoBack?: () => void;\n\n renderFormFields: (\n props: Pick<\n UseFormReturn<FormData>,\n 'register' | 'setValue' | 'formState'\n > & {\n values: UnpackNestedValue<FormData>;\n groups: string[];\n groupsLoading: boolean;\n },\n ) => React.ReactNode;\n};\n\nexport function generateEntities(\n entities: PartialEntity[],\n componentName: string,\n owner?: string,\n): Entity[] {\n return entities.map(e => ({\n ...e,\n apiVersion: e.apiVersion!,\n kind: e.kind!,\n metadata: {\n ...e.metadata,\n name: componentName,\n },\n spec: {\n ...e.spec,\n ...(owner ? { owner } : {}),\n },\n }));\n}\n\nexport const StepPrepareCreatePullRequest = ({\n analyzeResult,\n onPrepare,\n onGoBack,\n renderFormFields,\n}: Props) => {\n const classes = useStyles();\n const catalogApi = useApi(catalogApiRef);\n const catalogImportApi = useApi(catalogImportApiRef);\n const errorApi = useApi(errorApiRef);\n\n const [submitted, setSubmitted] = useState(false);\n const [error, setError] = useState<string>();\n\n const {\n loading: prDefaultsLoading,\n value: prDefaults,\n error: prDefaultsError,\n } = useAsync(\n () => catalogImportApi.preparePullRequest!(),\n [catalogImportApi.preparePullRequest],\n );\n\n useEffect(() => {\n if (prDefaultsError) {\n errorApi.post(prDefaultsError);\n }\n }, [prDefaultsError, errorApi]);\n\n const { loading: groupsLoading, value: groups } = useAsync(async () => {\n const groupEntities = await catalogApi.getEntities({\n filter: { kind: 'group' },\n });\n\n return groupEntities.items\n .map(e => formatEntityRefTitle(e, { defaultKind: 'group' }))\n .sort();\n });\n\n const handleResult = useCallback(\n async (data: FormData) => {\n setSubmitted(true);\n\n try {\n const pr = await catalogImportApi.submitPullRequest({\n repositoryUrl: analyzeResult.url,\n title: data.title,\n body: data.body,\n fileContent: generateEntities(\n analyzeResult.generatedEntities,\n data.componentName,\n data.owner,\n )\n .map(e => YAML.stringify(e))\n .join('---\\n'),\n });\n\n onPrepare(\n {\n type: 'repository',\n url: analyzeResult.url,\n integrationType: analyzeResult.integrationType,\n pullRequest: {\n url: pr.link,\n },\n locations: [\n {\n target: pr.location,\n entities: generateEntities(\n analyzeResult.generatedEntities,\n data.componentName,\n data.owner,\n ).map(e => ({\n kind: e.kind,\n namespace: e.metadata.namespace!,\n name: e.metadata.name,\n })),\n },\n ],\n },\n { notRepeatable: true },\n );\n } catch (e) {\n setError(e.message);\n setSubmitted(false);\n }\n },\n [\n analyzeResult.generatedEntities,\n analyzeResult.integrationType,\n analyzeResult.url,\n catalogImportApi,\n onPrepare,\n ],\n );\n\n return (\n <>\n <Typography>\n You entered a link to a {analyzeResult.integrationType} repository but a{' '}\n <code>catalog-info.yaml</code> could not be found. Use this form to open\n a Pull Request that creates one.\n </Typography>\n\n {!prDefaultsLoading && (\n <PreparePullRequestForm<FormData>\n onSubmit={handleResult}\n defaultValues={{\n title: prDefaults?.title ?? '',\n body: prDefaults?.body ?? '',\n owner:\n (analyzeResult.generatedEntities[0]?.spec?.owner as string) || '',\n componentName:\n analyzeResult.generatedEntities[0]?.metadata?.name || '',\n useCodeowners: false,\n }}\n render={({ values, formState, register, setValue }) => (\n <>\n {renderFormFields({\n values,\n formState,\n register,\n setValue,\n groups: groups ?? [],\n groupsLoading,\n })}\n\n <Box marginTop={2}>\n <Typography variant=\"h6\">Preview Pull Request</Typography>\n </Box>\n\n <PreviewPullRequestComponent\n title={values.title}\n description={values.body}\n classes={{\n card: classes.previewCard,\n cardContent: classes.previewCardContent,\n }}\n />\n\n <Box marginTop={2} marginBottom={1}>\n <Typography variant=\"h6\">Preview Entities</Typography>\n </Box>\n\n <PreviewCatalogInfoComponent\n entities={generateEntities(\n analyzeResult.generatedEntities,\n values.componentName,\n values.owner,\n )}\n repositoryUrl={analyzeResult.url}\n classes={{\n card: classes.previewCard,\n cardContent: classes.previewCardContent,\n }}\n />\n\n {error && <FormHelperText error>{error}</FormHelperText>}\n\n <Grid container spacing={0}>\n {onGoBack && (\n <BackButton onClick={onGoBack} disabled={submitted} />\n )}\n <NextButton\n type=\"submit\"\n disabled={Boolean(\n formState.errors.title ||\n formState.errors.body ||\n formState.errors.owner,\n )}\n loading={submitted}\n >\n Create PR\n </NextButton>\n </Grid>\n </>\n )}\n />\n )}\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Checkbox,\n Grid,\n ListItem,\n ListItemIcon,\n ListItemText,\n Typography,\n} from '@material-ui/core';\nimport LocationOnIcon from '@material-ui/icons/LocationOn';\nimport React, { useCallback, useState } from 'react';\nimport { AnalyzeResult } from '../../api';\nimport { BackButton, NextButton } from '../Buttons';\nimport { EntityListComponent } from '../EntityListComponent';\nimport { PrepareResult } from '../useImportState';\nimport partition from 'lodash/partition';\n\ntype Props = {\n analyzeResult: Extract<AnalyzeResult, { type: 'locations' }>;\n prepareResult?: PrepareResult;\n onPrepare: (result: PrepareResult) => void;\n onGoBack?: () => void;\n};\n\n/**\n * A form that lets a user select one of a list of locations to import\n *\n * @param analyzeResult the result of the analysis\n * @param prepareResult the selectected locations from a previous step\n * @param onPrepare called after the selection\n * @param onGoBack called to go back to the previous step\n */\nexport const StepPrepareSelectLocations = ({\n analyzeResult,\n prepareResult,\n onPrepare,\n onGoBack,\n}: Props) => {\n const [selectedUrls, setSelectedUrls] = useState<string[]>(\n prepareResult?.locations.map(l => l.target) || [],\n );\n\n const [existingLocations, locations] = partition(\n analyzeResult?.locations,\n l => l.exists,\n );\n\n const handleResult = useCallback(async () => {\n onPrepare({\n type: 'locations',\n locations: locations.filter((l: any) => selectedUrls.includes(l.target)),\n });\n }, [locations, onPrepare, selectedUrls]);\n\n const onItemClick = (url: string) => {\n setSelectedUrls(urls =>\n urls.includes(url) ? urls.filter(u => u !== url) : urls.concat(url),\n );\n };\n\n const onSelectAll = () => {\n setSelectedUrls(urls =>\n urls.length < locations.length ? locations.map(l => l.target) : [],\n );\n };\n\n return (\n <>\n {locations.length > 0 && (\n <>\n <Typography>\n Select one or more locations that are present in your git\n repository:\n </Typography>\n <EntityListComponent\n firstListItem={\n <ListItem dense button onClick={onSelectAll}>\n <ListItemIcon>\n <Checkbox\n edge=\"start\"\n checked={selectedUrls.length === locations.length}\n indeterminate={\n selectedUrls.length > 0 &&\n selectedUrls.length < locations.length\n }\n tabIndex={-1}\n disableRipple\n />\n </ListItemIcon>\n <ListItemText primary=\"Select All\" />\n </ListItem>\n }\n onItemClick={onItemClick}\n locations={locations}\n locationListItemIcon={target => (\n <Checkbox\n edge=\"start\"\n checked={selectedUrls.includes(target)}\n tabIndex={-1}\n disableRipple\n />\n )}\n collapsed\n />\n </>\n )}\n\n {existingLocations.length > 0 && (\n <>\n <Typography>These locations already exist in the catalog:</Typography>\n <EntityListComponent\n locations={existingLocations}\n locationListItemIcon={() => <LocationOnIcon />}\n withLinks\n collapsed\n />\n </>\n )}\n\n <Grid container spacing={0}>\n {onGoBack && <BackButton onClick={onGoBack} />}\n <NextButton disabled={selectedUrls.length === 0} onClick={handleResult}>\n Review\n </NextButton>\n </Grid>\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\nimport { FormHelperText, Grid, Typography } from '@material-ui/core';\nimport LocationOnIcon from '@material-ui/icons/LocationOn';\nimport React, { useCallback, useState } from 'react';\nimport { BackButton, NextButton } from '../Buttons';\nimport { EntityListComponent } from '../EntityListComponent';\nimport { PrepareResult, ReviewResult } from '../useImportState';\n\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Link } from '@backstage/core-components';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\n\ntype Props = {\n prepareResult: PrepareResult;\n onReview: (result: ReviewResult) => void;\n onGoBack?: () => void;\n};\n\nexport const StepReviewLocation = ({\n prepareResult,\n onReview,\n onGoBack,\n}: Props) => {\n const catalogApi = useApi(catalogApiRef);\n const configApi = useApi(configApiRef);\n\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n\n const [submitted, setSubmitted] = useState(false);\n const [error, setError] = useState<string>();\n const exists =\n prepareResult.type === 'locations' &&\n prepareResult.locations.some(l => l.exists)\n ? true\n : false;\n const handleClick = useCallback(async () => {\n setSubmitted(true);\n try {\n let refreshed = new Array<{ target: string }>();\n if (prepareResult.type === 'locations') {\n refreshed = await Promise.all(\n prepareResult.locations\n .filter(l => l.exists)\n .map(async l => {\n const ref = stringifyEntityRef(l.entities[0] ?? l);\n await catalogApi.refreshEntity(ref);\n return { target: l.target };\n }),\n );\n }\n\n const locations = await Promise.all(\n prepareResult.locations\n .filter((l: unknown) => !(l as { exists?: boolean }).exists)\n .map(async l => {\n const result = await catalogApi.addLocation({\n type: 'url',\n target: l.target,\n presence:\n prepareResult.type === 'repository' ? 'optional' : 'required',\n });\n return {\n target: result.location.target,\n entities: result.entities,\n };\n }),\n );\n\n onReview({\n ...prepareResult,\n ...{ refreshed },\n locations,\n });\n } catch (e) {\n // TODO: this error should be handled differently. We add it as 'optional' and\n // it is not uncommon that a PR has not been merged yet.\n if (\n prepareResult.type === 'repository' &&\n e.message.startsWith(\n 'Location was added but has no entities specified yet',\n )\n ) {\n onReview({\n ...prepareResult,\n locations: prepareResult.locations.map(l => ({\n target: l.target,\n entities: [],\n })),\n });\n } else {\n setError(e.message);\n setSubmitted(false);\n }\n }\n }, [prepareResult, onReview, catalogApi]);\n\n return (\n <>\n {prepareResult.type === 'repository' && (\n <>\n <Typography paragraph>\n The following Pull Request has been opened:{' '}\n <Link\n to={prepareResult.pullRequest.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {prepareResult.pullRequest.url}\n </Link>\n </Typography>\n\n <Typography paragraph>\n You can already import the location and {appTitle} will fetch the\n entities as soon as the Pull Request is merged.\n </Typography>\n </>\n )}\n\n <Typography>\n {exists\n ? 'The following locations already exist in the catalog:'\n : 'The following entities will be added to the catalog:'}\n </Typography>\n\n <EntityListComponent\n locations={prepareResult.locations}\n locationListItemIcon={() => <LocationOnIcon />}\n />\n\n {error && <FormHelperText error>{error}</FormHelperText>}\n\n <Grid container spacing={0}>\n {onGoBack && <BackButton onClick={onGoBack} disabled={submitted} />}\n <NextButton\n disabled={submitted}\n loading={submitted}\n onClick={() => handleClick()}\n >\n {exists ? 'Refresh' : 'Import'}\n </NextButton>\n </Grid>\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n FormHelperText,\n StepLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport React from 'react';\nimport { BackButton } from '../Buttons';\nimport { asInputRef } from '../helpers';\nimport { StepFinishImportLocation } from '../StepFinishImportLocation';\nimport { StepInitAnalyzeUrl } from '../StepInitAnalyzeUrl';\nimport {\n AutocompleteTextField,\n StepPrepareCreatePullRequest,\n} from '../StepPrepareCreatePullRequest';\nimport { StepPrepareSelectLocations } from '../StepPrepareSelectLocations';\nimport { StepReviewLocation } from '../StepReviewLocation';\nimport { StepperApis } from '../types';\nimport { ImportFlows, ImportState } from '../useImportState';\n\nexport type StepConfiguration = {\n stepLabel: React.ReactElement;\n content: React.ReactElement;\n};\n\nexport type StepperProvider = {\n analyze: (\n s: Extract<ImportState, { activeState: 'analyze' }>,\n opts: { apis: StepperApis },\n ) => StepConfiguration;\n prepare: (\n s: Extract<ImportState, { activeState: 'prepare' }>,\n opts: { apis: StepperApis },\n ) => StepConfiguration;\n review: (\n s: Extract<ImportState, { activeState: 'review' }>,\n opts: { apis: StepperApis },\n ) => StepConfiguration;\n finish: (\n s: Extract<ImportState, { activeState: 'finish' }>,\n opts: { apis: StepperApis },\n ) => StepConfiguration;\n};\n\n/**\n * The default stepper generation function.\n *\n * Override this function to customize the import flow. Each flow should at\n * least override the prepare operation.\n *\n * @param flow the name of the active flow\n * @param defaults the default steps\n */\nexport function defaultGenerateStepper(\n flow: ImportFlows,\n defaults: StepperProvider,\n): StepperProvider {\n switch (flow) {\n // the prepare step is skipped but the label of the step is updated\n case 'single-location':\n return {\n ...defaults,\n prepare: () => ({\n stepLabel: (\n <StepLabel\n optional={\n <Typography variant=\"caption\">\n Discovered Locations: 1\n </Typography>\n }\n >\n Select Locations\n </StepLabel>\n ),\n content: <></>,\n }),\n };\n\n // let the user select one or more of the discovered locations in the prepare step\n case 'multiple-locations':\n return {\n ...defaults,\n prepare: (state, opts) => {\n if (state.analyzeResult.type !== 'locations') {\n return defaults.prepare(state, opts);\n }\n\n return {\n stepLabel: (\n <StepLabel\n optional={\n <Typography variant=\"caption\">\n Discovered Locations: {state.analyzeResult.locations.length}\n </Typography>\n }\n >\n Select Locations\n </StepLabel>\n ),\n content: (\n <StepPrepareSelectLocations\n analyzeResult={state.analyzeResult}\n prepareResult={state.prepareResult}\n onPrepare={state.onPrepare}\n onGoBack={state.onGoBack}\n />\n ),\n };\n },\n };\n\n case 'no-location':\n return {\n ...defaults,\n prepare: (state, opts) => {\n if (state.analyzeResult.type !== 'repository') {\n return defaults.prepare(state, opts);\n }\n\n return {\n stepLabel: <StepLabel>Create Pull Request</StepLabel>,\n content: (\n <StepPrepareCreatePullRequest\n analyzeResult={state.analyzeResult}\n onPrepare={state.onPrepare}\n onGoBack={state.onGoBack}\n renderFormFields={({\n values,\n setValue,\n formState,\n groupsLoading,\n groups,\n register,\n }) => (\n <>\n <Box marginTop={2}>\n <Typography variant=\"h6\">Pull Request Details</Typography>\n </Box>\n\n <TextField\n {...asInputRef(\n register('title', {\n required: true,\n }),\n )}\n label=\"Pull Request Title\"\n placeholder=\"Add Backstage catalog entity descriptor files\"\n margin=\"normal\"\n variant=\"outlined\"\n fullWidth\n error={Boolean(formState.errors.title)}\n required\n />\n\n <TextField\n {...asInputRef(\n register('body', {\n required: true,\n }),\n )}\n label=\"Pull Request Body\"\n placeholder=\"A describing text with Markdown support\"\n margin=\"normal\"\n variant=\"outlined\"\n fullWidth\n error={Boolean(formState.errors.body)}\n multiline\n required\n />\n\n <Box marginTop={2}>\n <Typography variant=\"h6\">Entity Configuration</Typography>\n </Box>\n\n <TextField\n {...asInputRef(\n register('componentName', { required: true }),\n )}\n label=\"Name of the created component\"\n placeholder=\"my-component\"\n margin=\"normal\"\n variant=\"outlined\"\n fullWidth\n error={Boolean(formState.errors.componentName)}\n required\n />\n\n {!values.useCodeowners && (\n <AutocompleteTextField\n name=\"owner\"\n errors={formState.errors}\n options={groups || []}\n loading={groupsLoading}\n loadingText=\"Loading groups…\"\n helperText=\"Select an owner from the list or enter a reference to a Group or a User\"\n errorHelperText=\"required value\"\n textFieldProps={{\n label: 'Entity Owner',\n placeholder: 'my-group',\n }}\n rules={{ required: true }}\n required\n />\n )}\n\n <FormControlLabel\n control={\n <Checkbox\n {...asInputRef(register('useCodeowners'))}\n onChange={(_, value) => {\n if (value) {\n setValue('owner', '');\n }\n }}\n />\n }\n label={\n <>\n Use <em>CODEOWNERS</em> file as Entity Owner\n </>\n }\n />\n <FormHelperText>\n WARNING: This may fail if no CODEOWNERS file is found at\n the target location.\n </FormHelperText>\n </>\n )}\n />\n ),\n };\n },\n };\n\n default:\n return defaults;\n }\n}\n\nexport const defaultStepper: StepperProvider = {\n analyze: (state, { apis }) => ({\n stepLabel: <StepLabel>Select URL</StepLabel>,\n content: (\n <StepInitAnalyzeUrl\n key=\"analyze\"\n analysisUrl={state.analysisUrl}\n onAnalysis={state.onAnalysis}\n disablePullRequest={!apis.catalogImportApi.preparePullRequest}\n />\n ),\n }),\n\n prepare: state => ({\n stepLabel: (\n <StepLabel optional={<Typography variant=\"caption\">Optional</Typography>}>\n Import Actions\n </StepLabel>\n ),\n content: <BackButton onClick={state.onGoBack} />,\n }),\n\n review: state => ({\n stepLabel: <StepLabel>Review</StepLabel>,\n content: (\n <StepReviewLocation\n prepareResult={state.prepareResult}\n onReview={state.onReview}\n onGoBack={state.onGoBack}\n />\n ),\n }),\n\n finish: state => ({\n stepLabel: <StepLabel>Finish</StepLabel>,\n content: (\n <StepFinishImportLocation\n prepareResult={state.prepareResult}\n onReset={state.onReset}\n />\n ),\n }),\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InfoCard, InfoCardVariants } from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Step, StepContent, Stepper } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport React, { useMemo } from 'react';\nimport { catalogImportApiRef } from '../../api';\nimport { ImportFlows, ImportState, useImportState } from '../useImportState';\nimport {\n defaultGenerateStepper,\n defaultStepper,\n StepConfiguration,\n StepperProvider,\n} from './defaults';\n\nconst useStyles = makeStyles(() => ({\n stepperRoot: {\n padding: 0,\n },\n}));\n\ntype Props = {\n initialUrl?: string;\n generateStepper?: (\n flow: ImportFlows,\n defaults: StepperProvider,\n ) => StepperProvider;\n variant?: InfoCardVariants;\n};\n\nexport const ImportStepper = ({\n initialUrl,\n generateStepper = defaultGenerateStepper,\n variant,\n}: Props) => {\n const catalogImportApi = useApi(catalogImportApiRef);\n const classes = useStyles();\n const state = useImportState({ initialUrl });\n\n const states = useMemo<StepperProvider>(\n () => generateStepper(state.activeFlow, defaultStepper),\n [generateStepper, state.activeFlow],\n );\n\n const render = (step: StepConfiguration) => {\n return (\n <Step>\n {step.stepLabel}\n <StepContent>{step.content}</StepContent>\n </Step>\n );\n };\n\n return (\n <InfoCard variant={variant}>\n <Stepper\n classes={{ root: classes.stepperRoot }}\n activeStep={state.activeStepNumber}\n orientation=\"vertical\"\n >\n {render(\n states.analyze(\n state as Extract<ImportState, { activeState: 'analyze' }>,\n { apis: { catalogImportApi } },\n ),\n )}\n {render(\n states.prepare(\n state as Extract<ImportState, { activeState: 'prepare' }>,\n { apis: { catalogImportApi } },\n ),\n )}\n {render(\n states.review(\n state as Extract<ImportState, { activeState: 'review' }>,\n { apis: { catalogImportApi } },\n ),\n )}\n {render(\n states.finish(\n state as Extract<ImportState, { activeState: 'finish' }>,\n { apis: { catalogImportApi } },\n ),\n )}\n </Stepper>\n </InfoCard>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Content,\n ContentHeader,\n Header,\n Page,\n SupportButton,\n} from '@backstage/core-components';\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Grid } from '@material-ui/core';\nimport React from 'react';\nimport { ImportInfoCard } from '../ImportInfoCard';\nimport { ImportStepper } from '../ImportStepper';\n\nexport const DefaultImportPage = () => {\n const configApi = useApi(configApiRef);\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n\n return (\n <Page themeId=\"home\">\n <Header title=\"Register an existing component\" />\n <Content>\n <ContentHeader title={`Start tracking your component in ${appTitle}`}>\n <SupportButton>\n Start tracking your component in {appTitle} by adding it to the\n software catalog.\n </SupportButton>\n </ContentHeader>\n\n <Grid container spacing={2} direction=\"row-reverse\">\n <Grid item xs={12} md={4} lg={6} xl={8}>\n <ImportInfoCard />\n </Grid>\n\n <Grid item xs={12} md={8} lg={6} xl={4}>\n <ImportStepper />\n </Grid>\n </Grid>\n </Content>\n </Page>\n );\n};\n"],"names":["useStyles","this"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/api/CatalogImportApi.ts","../src/api/GitHub.ts","../src/api/CatalogImportClient.ts","../src/plugin.ts","../src/components/ImportInfoCard/ImportInfoCard.tsx","../src/components/useImportState.ts","../src/components/Buttons/index.tsx","../src/components/helpers.ts","../src/components/EntityListComponent/EntityListComponent.tsx","../src/components/StepFinishImportLocation/StepFinishImportLocation.tsx","../src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.tsx","../src/components/StepPrepareCreatePullRequest/AutocompleteTextField.tsx","../src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.tsx","../src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.tsx","../src/components/StepPrepareCreatePullRequest/PreviewPullRequestComponent.tsx","../src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.tsx","../src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.tsx","../src/components/StepReviewLocation/StepReviewLocation.tsx","../src/components/ImportStepper/defaults.tsx","../src/components/ImportStepper/ImportStepper.tsx","../src/components/DefaultImportPage/DefaultImportPage.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { EntityName } from '@backstage/catalog-model';\nimport { createApiRef } from '@backstage/core-plugin-api';\nimport { PartialEntity } from '../types';\n\nexport const catalogImportApiRef = createApiRef<CatalogImportApi>({\n id: 'plugin.catalog-import.service',\n});\n\n// result of the analyze state\nexport type AnalyzeResult =\n | {\n type: 'locations';\n locations: Array<{\n target: string;\n exists?: boolean;\n entities: EntityName[];\n }>;\n }\n | {\n type: 'repository';\n url: string;\n integrationType: string;\n generatedEntities: PartialEntity[];\n };\n\nexport interface CatalogImportApi {\n analyzeUrl(url: string): Promise<AnalyzeResult>;\n\n preparePullRequest?(): Promise<{\n title: string;\n body: string;\n }>;\n submitPullRequest(options: {\n repositoryUrl: string;\n fileContent: string;\n title: string;\n body: string;\n }): Promise<{ link: string; location: string }>;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport parseGitUrl from 'git-url-parse';\n\nexport const getGithubIntegrationConfig = (\n scmIntegrationsApi: ScmIntegrationRegistry,\n location: string,\n) => {\n const integration = scmIntegrationsApi.github.byUrl(location);\n if (!integration) {\n return undefined;\n }\n\n const { name: repo, owner } = parseGitUrl(location);\n return {\n repo,\n owner,\n githubIntegrationConfig: integration.config,\n };\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { EntityName } from '@backstage/catalog-model';\nimport {\n ConfigApi,\n DiscoveryApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\nimport {\n GitHubIntegrationConfig,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport { ScmAuthApi } from '@backstage/integration-react';\nimport { Octokit } from '@octokit/rest';\nimport { Base64 } from 'js-base64';\nimport { PartialEntity } from '../types';\nimport { AnalyzeResult, CatalogImportApi } from './CatalogImportApi';\nimport { getGithubIntegrationConfig } from './GitHub';\nimport { trimEnd } from 'lodash';\n\nexport class CatalogImportClient implements CatalogImportApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly identityApi: IdentityApi;\n private readonly scmAuthApi: ScmAuthApi;\n private readonly scmIntegrationsApi: ScmIntegrationRegistry;\n private readonly catalogApi: CatalogApi;\n private readonly configApi: ConfigApi;\n\n constructor(options: {\n discoveryApi: DiscoveryApi;\n scmAuthApi: ScmAuthApi;\n identityApi: IdentityApi;\n scmIntegrationsApi: ScmIntegrationRegistry;\n catalogApi: CatalogApi;\n configApi: ConfigApi;\n }) {\n this.discoveryApi = options.discoveryApi;\n this.scmAuthApi = options.scmAuthApi;\n this.identityApi = options.identityApi;\n this.scmIntegrationsApi = options.scmIntegrationsApi;\n this.catalogApi = options.catalogApi;\n this.configApi = options.configApi;\n }\n\n async analyzeUrl(url: string): Promise<AnalyzeResult> {\n if (\n new URL(url).pathname.match(/\\.ya?ml$/) ||\n new URL(url).searchParams.get('path')?.match(/.ya?ml$/)\n ) {\n const location = await this.catalogApi.addLocation({\n type: 'url',\n target: url,\n dryRun: true,\n });\n\n return {\n type: 'locations',\n locations: [\n {\n exists: location.exists,\n target: location.location.target,\n entities: location.entities.map(e => ({\n kind: e.kind,\n namespace: e.metadata.namespace ?? 'default',\n name: e.metadata.name,\n })),\n },\n ],\n };\n }\n\n const ghConfig = getGithubIntegrationConfig(this.scmIntegrationsApi, url);\n if (!ghConfig) {\n const other = this.scmIntegrationsApi.byUrl(url);\n if (other) {\n throw new Error(\n `The ${other.title} integration only supports full URLs to catalog-info.yaml files. Did you try to pass in the URL of a directory instead?`,\n );\n }\n throw new Error(\n 'This URL was not recognized as a valid GitHub URL because there was no configured integration that matched the given host name. You could try to paste the full URL to a catalog-info.yaml file instead.',\n );\n }\n\n // TODO: this could be part of the analyze-location endpoint\n const locations = await this.checkGitHubForExistingCatalogInfo({\n ...ghConfig,\n url,\n });\n\n if (locations.length > 0) {\n return {\n type: 'locations',\n locations,\n };\n }\n\n return {\n type: 'repository',\n integrationType: 'github',\n url: url,\n generatedEntities: await this.generateEntityDefinitions({\n repo: url,\n }),\n };\n }\n\n async preparePullRequest(): Promise<{\n title: string;\n body: string;\n }> {\n const appTitle =\n this.configApi.getOptionalString('app.title') ?? 'Backstage';\n const appBaseUrl = this.configApi.getString('app.baseUrl');\n\n return {\n title: 'Add catalog-info.yaml config file',\n body: `This pull request adds a **Backstage entity metadata file** \\\nto this repository so that the component can be added to the \\\n[${appTitle} software catalog](${appBaseUrl}).\\n\\nAfter this pull request is merged, \\\nthe component will become available.\\n\\nFor more information, read an \\\n[overview of the Backstage software catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview).`,\n };\n }\n\n async submitPullRequest({\n repositoryUrl,\n fileContent,\n title,\n body,\n }: {\n repositoryUrl: string;\n fileContent: string;\n title: string;\n body: string;\n }): Promise<{ link: string; location: string }> {\n const ghConfig = getGithubIntegrationConfig(\n this.scmIntegrationsApi,\n repositoryUrl,\n );\n\n if (ghConfig) {\n return await this.submitGitHubPrToRepo({\n ...ghConfig,\n repositoryUrl,\n fileContent,\n title,\n body,\n });\n }\n\n throw new Error('unimplemented!');\n }\n\n // TODO: this could be part of the catalog api\n private async generateEntityDefinitions({\n repo,\n }: {\n repo: string;\n }): Promise<PartialEntity[]> {\n const idToken = await this.identityApi.getIdToken();\n const response = await fetch(\n `${await this.discoveryApi.getBaseUrl('catalog')}/analyze-location`,\n {\n headers: {\n 'Content-Type': 'application/json',\n ...(idToken && { Authorization: `Bearer ${idToken}` }),\n },\n method: 'POST',\n body: JSON.stringify({\n location: { type: 'url', target: repo },\n }),\n },\n ).catch(e => {\n throw new Error(`Failed to generate entity definitions, ${e.message}`);\n });\n if (!response.ok) {\n throw new Error(\n `Failed to generate entity definitions. Received http response ${response.status}: ${response.statusText}`,\n );\n }\n\n const payload = await response.json();\n return payload.generateEntities.map((x: any) => x.entity);\n }\n\n // TODO: this response should better be part of the analyze-locations response and scm-independent / implemented per scm\n private async checkGitHubForExistingCatalogInfo({\n url,\n owner,\n repo,\n githubIntegrationConfig,\n }: {\n url: string;\n owner: string;\n repo: string;\n githubIntegrationConfig: GitHubIntegrationConfig;\n }): Promise<\n Array<{\n target: string;\n entities: EntityName[];\n }>\n > {\n const { token } = await this.scmAuthApi.getCredentials({ url });\n const octo = new Octokit({\n auth: token,\n baseUrl: githubIntegrationConfig.apiBaseUrl,\n });\n const catalogFileName = 'catalog-info.yaml';\n const query = `repo:${owner}/${repo}+filename:${catalogFileName}`;\n\n const searchResult = await octo.search.code({ q: query }).catch(e => {\n throw new Error(\n formatHttpErrorMessage(\n \"Couldn't search repository for metadata file.\",\n e,\n ),\n );\n });\n const exists = searchResult.data.total_count > 0;\n if (exists) {\n const repoInformation = await octo.repos.get({ owner, repo }).catch(e => {\n throw new Error(formatHttpErrorMessage(\"Couldn't fetch repo data\", e));\n });\n const defaultBranch = repoInformation.data.default_branch;\n\n return await Promise.all(\n searchResult.data.items\n .map(i => `${trimEnd(url, '/')}/blob/${defaultBranch}/${i.path}`)\n .map(async target => {\n const result = await this.catalogApi.addLocation({\n type: 'url',\n target,\n dryRun: true,\n });\n return {\n target,\n exists: result.exists,\n entities: result.entities.map(e => ({\n kind: e.kind,\n namespace: e.metadata.namespace ?? 'default',\n name: e.metadata.name,\n })),\n };\n }),\n );\n }\n\n return [];\n }\n\n // TODO: extract this function and implement for non-github\n private async submitGitHubPrToRepo({\n owner,\n repo,\n title,\n body,\n fileContent,\n repositoryUrl,\n githubIntegrationConfig,\n }: {\n owner: string;\n repo: string;\n title: string;\n body: string;\n fileContent: string;\n repositoryUrl: string;\n githubIntegrationConfig: GitHubIntegrationConfig;\n }): Promise<{ link: string; location: string }> {\n const { token } = await this.scmAuthApi.getCredentials({\n url: repositoryUrl,\n additionalScope: {\n repoWrite: true,\n },\n });\n\n const octo = new Octokit({\n auth: token,\n baseUrl: githubIntegrationConfig.apiBaseUrl,\n });\n\n const branchName = 'backstage-integration';\n const fileName = 'catalog-info.yaml';\n\n const repoData = await octo.repos\n .get({\n owner,\n repo,\n })\n .catch(e => {\n throw new Error(formatHttpErrorMessage(\"Couldn't fetch repo data\", e));\n });\n\n const parentRef = await octo.git\n .getRef({\n owner,\n repo,\n ref: `heads/${repoData.data.default_branch}`,\n })\n .catch(e => {\n throw new Error(\n formatHttpErrorMessage(\"Couldn't fetch default branch data\", e),\n );\n });\n\n await octo.git\n .createRef({\n owner,\n repo,\n ref: `refs/heads/${branchName}`,\n sha: parentRef.data.object.sha,\n })\n .catch(e => {\n throw new Error(\n formatHttpErrorMessage(\n `Couldn't create a new branch with name '${branchName}'`,\n e,\n ),\n );\n });\n\n await octo.repos\n .createOrUpdateFileContents({\n owner,\n repo,\n path: fileName,\n message: title,\n content: Base64.encode(fileContent),\n branch: branchName,\n })\n .catch(e => {\n throw new Error(\n formatHttpErrorMessage(\n `Couldn't create a commit with ${fileName} file added`,\n e,\n ),\n );\n });\n\n const pullRequestResponse = await octo.pulls\n .create({\n owner,\n repo,\n title,\n head: branchName,\n body,\n base: repoData.data.default_branch,\n })\n .catch(e => {\n throw new Error(\n formatHttpErrorMessage(\n `Couldn't create a pull request for ${branchName} branch`,\n e,\n ),\n );\n });\n\n return {\n link: pullRequestResponse.data.html_url,\n location: `https://${githubIntegrationConfig.host}/${owner}/${repo}/blob/${repoData.data.default_branch}/${fileName}`,\n };\n }\n}\n\nfunction formatHttpErrorMessage(\n message: string,\n error: { status: number; message: string },\n) {\n return `${message}, received http response status code ${error.status}: ${error.message}`;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n configApiRef,\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n createRouteRef,\n discoveryApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\nimport {\n scmAuthApiRef,\n scmIntegrationsApiRef,\n} from '@backstage/integration-react';\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\nimport { catalogImportApiRef, CatalogImportClient } from './api';\n\nexport const rootRouteRef = createRouteRef({\n id: 'catalog-import',\n});\n\nexport const catalogImportPlugin = createPlugin({\n id: 'catalog-import',\n apis: [\n createApiFactory({\n api: catalogImportApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n scmAuthApi: scmAuthApiRef,\n identityApi: identityApiRef,\n scmIntegrationsApi: scmIntegrationsApiRef,\n catalogApi: catalogApiRef,\n configApi: configApiRef,\n },\n factory: ({\n discoveryApi,\n scmAuthApi,\n identityApi,\n scmIntegrationsApi,\n catalogApi,\n configApi,\n }) =>\n new CatalogImportClient({\n discoveryApi,\n scmAuthApi,\n scmIntegrationsApi,\n identityApi,\n catalogApi,\n configApi,\n }),\n }),\n ],\n routes: {\n importPage: rootRouteRef,\n },\n});\n\nexport const CatalogImportPage = catalogImportPlugin.provide(\n createRoutableExtension({\n name: 'CatalogImportPage',\n component: () => import('./components/ImportPage').then(m => m.ImportPage),\n mountPoint: rootRouteRef,\n }),\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InfoCard } from '@backstage/core-components';\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Chip, Typography } from '@material-ui/core';\nimport React from 'react';\nimport { catalogImportApiRef } from '../../api';\n\nexport const ImportInfoCard = () => {\n const configApi = useApi(configApiRef);\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n const catalogImportApi = useApi(catalogImportApiRef);\n\n const integrations = configApi.getConfig('integrations');\n const hasGithubIntegration = integrations.has('github');\n\n return (\n <InfoCard\n title=\"Register an existing component\"\n deepLink={{\n title: 'Learn more about the Software Catalog',\n link: 'https://backstage.io/docs/features/software-catalog/software-catalog-overview',\n }}\n >\n <Typography variant=\"body2\" paragraph>\n Enter the URL to your source code repository to add it to {appTitle}.\n </Typography>\n <Typography variant=\"h6\">Link to an existing entity file</Typography>\n <Typography variant=\"subtitle2\" color=\"textSecondary\" paragraph>\n Example:{' '}\n <code>\n https://github.com/backstage/backstage/blob/master/catalog-info.yaml\n </code>\n </Typography>\n <Typography variant=\"body2\" paragraph>\n The wizard analyzes the file, previews the entities, and adds them to\n the {appTitle} catalog.\n </Typography>\n {hasGithubIntegration && (\n <>\n <Typography variant=\"h6\">\n Link to a repository{' '}\n <Chip label=\"GitHub only\" variant=\"outlined\" size=\"small\" />\n </Typography>\n <Typography variant=\"subtitle2\" color=\"textSecondary\" paragraph>\n Example: <code>https://github.com/backstage/backstage</code>\n </Typography>\n <Typography variant=\"body2\" paragraph>\n The wizard discovers all <code>catalog-info.yaml</code> files in the\n repository, previews the entities, and adds them to the {appTitle}{' '}\n catalog.\n </Typography>\n {catalogImportApi.preparePullRequest && (\n <Typography variant=\"body2\" paragraph>\n If no entities are found, the wizard will prepare a Pull Request\n that adds an example <code>catalog-info.yaml</code> and prepares\n the {appTitle} catalog to load all entities as soon as the Pull\n Request is merged.\n </Typography>\n )}\n </>\n )}\n </InfoCard>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, EntityName } from '@backstage/catalog-model';\nimport { useReducer } from 'react';\nimport { AnalyzeResult } from '../api';\n\n// the configuration of the stepper\nexport type ImportFlows =\n | 'unknown'\n | 'single-location'\n | 'multiple-locations'\n | 'no-location';\n\n// the available states of the stepper\ntype ImportStateTypes = 'analyze' | 'prepare' | 'review' | 'finish';\n\n// result of the prepare state\nexport type PrepareResult =\n | {\n type: 'locations';\n locations: Array<{\n exists?: boolean;\n target: string;\n entities: EntityName[];\n }>;\n }\n | {\n type: 'repository';\n url: string;\n integrationType: string;\n pullRequest: {\n url: string;\n };\n locations: Array<{\n target: string;\n entities: EntityName[];\n }>;\n };\n\n// result of the review result\nexport type ReviewResult =\n | {\n type: 'locations';\n locations: Array<{\n target: string;\n entities: Entity[];\n }>;\n refreshed: Array<{ target: string }>;\n }\n | {\n type: 'repository';\n url: string;\n integrationType: string;\n pullRequest: {\n url: string;\n };\n locations: Array<{\n target: string;\n entities: Entity[];\n }>;\n };\n\n// function type for the 'analysis' -> 'prepare'/'review' transition\ntype onAnalysisFn = (\n flow: ImportFlows,\n url: string,\n result: AnalyzeResult,\n opts?: { prepareResult?: PrepareResult },\n) => void;\n\n// function type for the 'prepare' -> 'review' transition\ntype onPrepareFn = (\n result: PrepareResult,\n opts?: { notRepeatable?: boolean },\n) => void;\n\n// function type for the 'review' -> 'finish' transition\ntype onReviewFn = (result: ReviewResult) => void;\n\n// the type interfaces that are available in each state. every state provides\n// already known information and means to go to the next, or the previous step.\ntype State =\n | {\n activeState: 'analyze';\n onAnalysis: onAnalysisFn;\n }\n | {\n activeState: 'prepare';\n analyzeResult: AnalyzeResult;\n prepareResult?: PrepareResult;\n onPrepare: onPrepareFn;\n }\n | {\n activeState: 'review';\n analyzeResult: AnalyzeResult;\n prepareResult: PrepareResult;\n onReview: onReviewFn;\n }\n | {\n activeState: 'finish';\n analyzeResult: AnalyzeResult;\n prepareResult: PrepareResult;\n reviewResult: ReviewResult;\n };\n\nexport type ImportState = State & {\n activeFlow: ImportFlows;\n activeStepNumber: number;\n analysisUrl?: string;\n\n onGoBack?: () => void;\n onReset: () => void;\n};\n\ntype ReducerActions =\n | { type: 'onAnalysis'; args: Parameters<onAnalysisFn> }\n | { type: 'onPrepare'; args: Parameters<onPrepareFn> }\n | { type: 'onReview'; args: Parameters<onReviewFn> }\n | { type: 'onGoBack' }\n | { type: 'onReset'; initialUrl?: string };\n\ntype ReducerState = {\n activeFlow: ImportFlows;\n activeState: ImportStateTypes;\n analysisUrl?: string;\n analyzeResult?: AnalyzeResult;\n prepareResult?: PrepareResult;\n reviewResult?: ReviewResult;\n\n previousStates: ImportStateTypes[];\n};\n\nfunction init(initialUrl?: string): ReducerState {\n return {\n activeFlow: 'unknown',\n activeState: 'analyze',\n analysisUrl: initialUrl,\n previousStates: [],\n };\n}\n\nfunction reducer(state: ReducerState, action: ReducerActions): ReducerState {\n switch (action.type) {\n case 'onAnalysis': {\n if (state.activeState !== 'analyze') {\n return state;\n }\n\n const { activeState, previousStates } = state;\n const [activeFlow, analysisUrl, analyzeResult, opts] = action.args;\n\n return {\n ...state,\n analysisUrl,\n activeFlow,\n analyzeResult,\n prepareResult: opts?.prepareResult,\n\n activeState: opts?.prepareResult === undefined ? 'prepare' : 'review',\n previousStates: previousStates.concat(activeState),\n };\n }\n\n case 'onPrepare': {\n if (state.activeState !== 'prepare') {\n return state;\n }\n\n const { activeState, previousStates } = state;\n const [prepareResult, opts] = action.args;\n\n return {\n ...state,\n prepareResult,\n\n activeState: 'review',\n previousStates: opts?.notRepeatable\n ? []\n : previousStates.concat(activeState),\n };\n }\n\n case 'onReview': {\n if (state.activeState !== 'review') {\n return state;\n }\n\n const { activeState, previousStates } = state;\n const [reviewResult] = action.args;\n\n return {\n ...state,\n reviewResult,\n\n activeState: 'finish',\n previousStates: previousStates.concat(activeState),\n };\n }\n\n case 'onGoBack': {\n const { activeState, previousStates } = state;\n\n return {\n ...state,\n\n activeState:\n previousStates.length > 0\n ? previousStates[previousStates.length - 1]\n : activeState,\n previousStates: previousStates.slice(0, previousStates.length - 1),\n };\n }\n\n case 'onReset':\n return {\n ...init(action.initialUrl),\n\n // we keep the old prepareResult since the form is animated and an\n // undefined value might crash the last step.\n prepareResult: state.prepareResult,\n };\n\n default:\n throw new Error();\n }\n}\n\n/**\n * A hook that manages the state machine of the form. It handles different flows\n * which each can implement up to four states:\n * 1. analyze\n * 2. preview (skippable from analyze->review)\n * 3. review\n * 4. finish\n *\n * @param options options\n */\nexport const useImportState = (options?: {\n initialUrl?: string;\n}): ImportState => {\n const [state, dispatch] = useReducer(reducer, options?.initialUrl, init);\n\n const { activeFlow, activeState, analysisUrl, previousStates } = state;\n\n return {\n activeFlow,\n activeState,\n activeStepNumber: ['analyze', 'prepare', 'review', 'finish'].indexOf(\n activeState,\n ),\n analysisUrl: analysisUrl,\n\n analyzeResult: state.analyzeResult!,\n prepareResult: state.prepareResult!,\n reviewResult: state.reviewResult!,\n\n onAnalysis: (flow, url, result, opts) =>\n dispatch({\n type: 'onAnalysis',\n args: [flow, url, result, opts],\n }),\n\n onPrepare: (result, opts) =>\n dispatch({\n type: 'onPrepare',\n args: [result, opts],\n }),\n\n onReview: result => dispatch({ type: 'onReview', args: [result] }),\n\n onGoBack:\n previousStates.length > 0\n ? () => dispatch({ type: 'onGoBack' })\n : undefined,\n\n onReset: () =>\n dispatch({ type: 'onReset', initialUrl: options?.initialUrl }),\n };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Button, CircularProgress } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport React, { ComponentProps } from 'react';\n\nconst useStyles = makeStyles(theme => ({\n wrapper: {\n marginTop: theme.spacing(1),\n marginRight: theme.spacing(1),\n position: 'relative',\n },\n buttonProgress: {\n position: 'absolute',\n top: '50%',\n left: '50%',\n marginTop: -12,\n marginLeft: -12,\n },\n button: {\n marginTop: theme.spacing(1),\n marginRight: theme.spacing(1),\n },\n}));\n\nexport const NextButton = (\n props: ComponentProps<typeof Button> & { loading?: boolean },\n) => {\n const { loading, ...buttonProps } = props;\n const classes = useStyles();\n\n return (\n <div className={classes.wrapper}>\n <Button\n color=\"primary\"\n variant=\"contained\"\n {...buttonProps}\n disabled={props.disabled || props.loading}\n />\n {props.loading && (\n <CircularProgress size=\"1.5rem\" className={classes.buttonProgress} />\n )}\n {props.loading}\n </div>\n );\n};\n\nexport const BackButton = (props: ComponentProps<typeof Button>) => {\n const classes = useStyles();\n\n return (\n <Button variant=\"outlined\" className={classes.button} {...props}>\n {props.children || 'Back'}\n </Button>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { UseFormRegisterReturn } from 'react-hook-form';\n\n/**\n * A helper that converts the result of a render('name', opts) to make it compatible with material-ui.\n *\n * See also https://github.com/react-hook-form/react-hook-form/issues/4629#issuecomment-815840872\n * TODO: remove when updating to material-ui v5 (https://github.com/mui-org/material-ui/pull/23174)\n *\n * @param renderResult - the result of a render('name', opts)\n */\nexport function asInputRef(renderResult: UseFormRegisterReturn) {\n const { ref, ...rest } = renderResult;\n return {\n inputRef: ref,\n ...rest,\n };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, EntityName } from '@backstage/catalog-model';\nimport { useApp } from '@backstage/core-plugin-api';\nimport {\n EntityRefLink,\n formatEntityRefTitle,\n} from '@backstage/plugin-catalog-react';\nimport {\n Collapse,\n IconButton,\n List,\n ListItem,\n ListItemIcon,\n ListItemSecondaryAction,\n ListItemText,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport ExpandLessIcon from '@material-ui/icons/ExpandLess';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport WorkIcon from '@material-ui/icons/Work';\nimport React, { useState } from 'react';\n\nconst useStyles = makeStyles(theme => ({\n nested: {\n paddingLeft: theme.spacing(4),\n },\n}));\n\nfunction sortEntities(entities: Array<EntityName | Entity>) {\n return entities.sort((a, b) =>\n formatEntityRefTitle(a).localeCompare(formatEntityRefTitle(b)),\n );\n}\n\ntype Props = {\n locations: Array<{ target: string; entities: (Entity | EntityName)[] }>;\n locationListItemIcon: (target: string) => React.ReactElement;\n collapsed?: boolean;\n firstListItem?: React.ReactElement;\n onItemClick?: (target: string) => void;\n withLinks?: boolean;\n};\n\nexport const EntityListComponent = ({\n locations,\n collapsed = false,\n locationListItemIcon,\n onItemClick,\n firstListItem,\n withLinks = false,\n}: Props) => {\n const app = useApp();\n const classes = useStyles();\n\n const [expandedUrls, setExpandedUrls] = useState<string[]>([]);\n\n const handleClick = (url: string) => {\n setExpandedUrls(urls =>\n urls.includes(url) ? urls.filter(u => u !== url) : urls.concat(url),\n );\n };\n\n return (\n <List>\n {firstListItem}\n {locations.map(r => (\n <React.Fragment key={r.target}>\n <ListItem\n dense\n button={Boolean(onItemClick) as any}\n onClick={() => onItemClick?.call(this, r.target)}\n >\n <ListItemIcon>{locationListItemIcon(r.target)}</ListItemIcon>\n\n <ListItemText\n primary={r.target}\n secondary={`Entities: ${r.entities.length}`}\n />\n\n {collapsed && (\n <ListItemSecondaryAction>\n <IconButton edge=\"end\" onClick={() => handleClick(r.target)}>\n {expandedUrls.includes(r.target) ? (\n <ExpandLessIcon />\n ) : (\n <ExpandMoreIcon />\n )}\n </IconButton>\n </ListItemSecondaryAction>\n )}\n </ListItem>\n\n <Collapse\n in={!collapsed || expandedUrls.includes(r.target)}\n timeout=\"auto\"\n unmountOnExit\n >\n <List component=\"div\" disablePadding dense>\n {sortEntities(r.entities).map(entity => {\n const Icon =\n app.getSystemIcon(\n `kind:${entity.kind.toLocaleLowerCase('en-US')}`,\n ) ?? WorkIcon;\n return (\n <ListItem\n key={formatEntityRefTitle(entity)}\n className={classes.nested}\n {...(withLinks\n ? {\n component: EntityRefLink,\n entityRef: entity,\n button: withLinks as any,\n }\n : {})}\n >\n <ListItemIcon>\n <Icon />\n </ListItemIcon>\n <ListItemText primary={formatEntityRefTitle(entity)} />\n </ListItem>\n );\n })}\n </List>\n </Collapse>\n </React.Fragment>\n ))}\n </List>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Grid, Typography } from '@material-ui/core';\nimport LocationOnIcon from '@material-ui/icons/LocationOn';\nimport React from 'react';\nimport { BackButton } from '../Buttons';\nimport { EntityListComponent } from '../EntityListComponent';\nimport { PrepareResult } from '../useImportState';\nimport { Link } from '@backstage/core-components';\nimport partition from 'lodash/partition';\n\ntype Props = {\n prepareResult: PrepareResult;\n onReset: () => void;\n};\n\nexport const StepFinishImportLocation = ({ prepareResult, onReset }: Props) => {\n const continueButton = (\n <Grid container spacing={0}>\n <BackButton onClick={onReset}>Register another</BackButton>\n </Grid>\n );\n\n if (prepareResult.type === 'repository') {\n return (\n <>\n <Typography paragraph>\n The following Pull Request has been opened:{' '}\n <Link\n to={prepareResult.pullRequest.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {prepareResult.pullRequest.url}\n </Link>\n </Typography>\n\n <Typography paragraph>\n Your entities will be imported as soon as the Pull Request is merged.\n </Typography>\n\n {continueButton}\n </>\n );\n }\n\n const [existingLocations, newLocations] = partition(\n prepareResult.locations,\n l => l.exists,\n );\n\n return (\n <>\n {newLocations.length > 0 && (\n <>\n <Typography>\n The following entities have been added to the catalog:\n </Typography>\n\n <EntityListComponent\n locations={newLocations}\n locationListItemIcon={() => <LocationOnIcon />}\n withLinks\n />\n </>\n )}\n {existingLocations.length > 0 && (\n <>\n <Typography>\n A refresh was triggered for the following locations:\n </Typography>\n\n <EntityListComponent\n locations={existingLocations}\n locationListItemIcon={() => <LocationOnIcon />}\n withLinks\n />\n </>\n )}\n {continueButton}\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport { FormHelperText, Grid, TextField } from '@material-ui/core';\nimport React, { useCallback, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { AnalyzeResult, catalogImportApiRef } from '../../api';\nimport { NextButton } from '../Buttons';\nimport { asInputRef } from '../helpers';\nimport { ImportFlows, PrepareResult } from '../useImportState';\n\ntype FormData = {\n url: string;\n};\n\ntype Props = {\n onAnalysis: (\n flow: ImportFlows,\n url: string,\n result: AnalyzeResult,\n opts?: { prepareResult?: PrepareResult },\n ) => void;\n disablePullRequest?: boolean;\n analysisUrl?: string;\n};\n\n/**\n * A form that lets the user input a url and analyze it for existing locations or potential entities.\n *\n * @param onAnalysis is called when the analysis was successful\n * @param analysisUrl a url that can be used as a default value\n * @param disablePullRequest if true, repositories without entities will abort the wizard\n */\nexport const StepInitAnalyzeUrl = ({\n onAnalysis,\n analysisUrl = '',\n disablePullRequest = false,\n}: Props) => {\n const errorApi = useApi(errorApiRef);\n const catalogImportApi = useApi(catalogImportApiRef);\n\n const {\n register,\n handleSubmit,\n formState: { errors },\n watch,\n } = useForm<FormData>({\n mode: 'onTouched',\n defaultValues: {\n url: analysisUrl,\n },\n });\n\n const [submitted, setSubmitted] = useState(false);\n const [error, setError] = useState<string | undefined>(undefined);\n\n const handleResult = useCallback(\n async ({ url }: FormData) => {\n setSubmitted(true);\n\n try {\n const analysisResult = await catalogImportApi.analyzeUrl(url);\n\n switch (analysisResult.type) {\n case 'repository':\n if (\n !disablePullRequest &&\n analysisResult.generatedEntities.length > 0\n ) {\n onAnalysis('no-location', url, analysisResult);\n } else {\n setError(\"Couldn't generate entities for your repository\");\n setSubmitted(false);\n }\n break;\n\n case 'locations': {\n if (analysisResult.locations.length === 1) {\n onAnalysis('single-location', url, analysisResult, {\n prepareResult: analysisResult,\n });\n } else if (analysisResult.locations.length > 1) {\n onAnalysis('multiple-locations', url, analysisResult);\n } else {\n setError('There are no entities at this location');\n setSubmitted(false);\n }\n break;\n }\n\n default: {\n const err = `Received unknown analysis result of type ${\n (analysisResult as any).type\n }. Please contact the support team.`;\n setError(err);\n setSubmitted(false);\n\n errorApi.post(new Error(err));\n break;\n }\n }\n } catch (e: any) {\n setError(e?.data?.error?.message ?? e.message);\n setSubmitted(false);\n }\n },\n [catalogImportApi, disablePullRequest, errorApi, onAnalysis],\n );\n\n return (\n <form onSubmit={handleSubmit(handleResult)}>\n <TextField\n {...asInputRef(\n register('url', {\n required: true,\n validate: {\n httpsValidator: (value: any) =>\n (typeof value === 'string' &&\n value.match(/^http[s]?:\\/\\//) !== null) ||\n 'Must start with http:// or https://.',\n },\n }),\n )}\n fullWidth\n id=\"url\"\n label=\"Repository URL\"\n placeholder=\"https://github.com/backstage/backstage/blob/master/catalog-info.yaml\"\n helperText=\"Enter the full path to your entity file to start tracking your component\"\n margin=\"normal\"\n variant=\"outlined\"\n error={Boolean(errors.url)}\n required\n />\n\n {errors.url && (\n <FormHelperText error>{errors.url.message}</FormHelperText>\n )}\n\n {error && <FormHelperText error>{error}</FormHelperText>}\n\n <Grid container spacing={0}>\n <NextButton\n disabled={Boolean(errors.url) || !watch('url')}\n loading={submitted}\n type=\"submit\"\n >\n Analyze\n </NextButton>\n </Grid>\n </form>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CircularProgress, TextField } from '@material-ui/core';\nimport { TextFieldProps } from '@material-ui/core/TextField/TextField';\nimport { Autocomplete } from '@material-ui/lab';\nimport React from 'react';\nimport { Controller, FieldErrors } from 'react-hook-form';\n\ntype Props<TFieldValue extends string> = {\n name: TFieldValue;\n options: string[];\n required?: boolean;\n\n errors?: FieldErrors;\n rules?: React.ComponentProps<typeof Controller>['rules'];\n\n loading?: boolean;\n loadingText?: string;\n\n helperText?: React.ReactNode;\n errorHelperText?: string;\n\n textFieldProps?: Omit<TextFieldProps, 'required' | 'fullWidth'>;\n};\n\nexport const AutocompleteTextField = <TFieldValue extends string>({\n name,\n options,\n required,\n errors,\n rules,\n loading = false,\n loadingText,\n helperText,\n errorHelperText,\n textFieldProps = {},\n}: Props<TFieldValue>) => {\n return (\n <Controller\n name={name}\n rules={rules}\n render={({ field: { onChange } }) => (\n <Autocomplete\n loading={loading}\n loadingText={loadingText}\n options={options || []}\n autoSelect\n freeSolo\n onChange={(_event: React.ChangeEvent<{}>, value: string | null) =>\n onChange(value)\n }\n renderInput={params => (\n <TextField\n {...params}\n helperText={(errors?.[name] && errorHelperText) || helperText}\n error={Boolean(errors?.[name])}\n margin=\"normal\"\n variant=\"outlined\"\n required={required}\n InputProps={{\n ...params.InputProps,\n endAdornment: (\n <React.Fragment>\n {loading ? (\n <CircularProgress color=\"inherit\" size=\"1em\" />\n ) : null}\n {params.InputProps.endAdornment}\n </React.Fragment>\n ),\n }}\n {...textFieldProps}\n />\n )}\n />\n )}\n />\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport {\n FormProvider,\n SubmitHandler,\n UnpackNestedValue,\n useForm,\n UseFormProps,\n UseFormReturn,\n} from 'react-hook-form';\n\ntype Props<TFieldValues extends Record<string, any>> = Pick<\n UseFormProps<TFieldValues>,\n 'defaultValues'\n> & {\n onSubmit: SubmitHandler<TFieldValues>;\n\n render: (\n props: Pick<\n UseFormReturn<TFieldValues>,\n 'formState' | 'register' | 'control' | 'setValue'\n > & {\n values: UnpackNestedValue<TFieldValues>;\n },\n ) => React.ReactNode;\n};\n\n/**\n * A form wrapper that creates a form that is used to prepare a pull request. It\n * hosts the form logic.\n *\n * @param defaultValues the default values of the form\n * @param onSubmit a callback that is executed when the form is submitted\n * (initiated by a button of type=\"submit\")\n * @param render render the form elements\n */\nexport const PreparePullRequestForm = <\n TFieldValues extends Record<string, any>,\n>({\n defaultValues,\n onSubmit,\n render,\n}: Props<TFieldValues>) => {\n const methods = useForm<TFieldValues>({ mode: 'onTouched', defaultValues });\n const { handleSubmit, watch, control, register, formState, setValue } =\n methods;\n\n return (\n <FormProvider {...methods}>\n <form onSubmit={handleSubmit(onSubmit)}>\n {render({ values: watch(), formState, register, control, setValue })}\n </form>\n </FormProvider>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { Card, CardContent, CardHeader } from '@material-ui/core';\nimport React from 'react';\nimport YAML from 'yaml';\nimport { CodeSnippet } from '@backstage/core-components';\nimport { trimEnd } from 'lodash';\n\ntype Props = {\n repositoryUrl: string;\n entities: Entity[];\n classes?: { card?: string; cardContent?: string };\n};\n\nexport const PreviewCatalogInfoComponent = ({\n repositoryUrl,\n entities,\n classes,\n}: Props) => {\n return (\n <Card variant=\"outlined\" className={classes?.card}>\n <CardHeader\n title={\n <code>{`${trimEnd(repositoryUrl, '/')}/catalog-info.yaml`}</code>\n }\n />\n\n <CardContent className={classes?.cardContent}>\n <CodeSnippet\n text={entities\n .map(e => YAML.stringify(e))\n .join('---\\n')\n .trim()}\n language=\"yaml\"\n />\n </CardContent>\n </Card>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Card, CardContent, CardHeader } from '@material-ui/core';\nimport React from 'react';\nimport { MarkdownContent } from '@backstage/core-components';\n\ntype Props = {\n title: string;\n description: string;\n classes?: { card?: string; cardContent?: string };\n};\n\nexport const PreviewPullRequestComponent = ({\n title,\n description,\n classes,\n}: Props) => {\n return (\n <Card variant=\"outlined\" className={classes?.card}>\n <CardHeader title={title} subheader=\"Create a new Pull Request\" />\n <CardContent className={classes?.cardContent}>\n <MarkdownContent content={description} />\n </CardContent>\n </Card>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport { assertError } from '@backstage/errors';\nimport {\n catalogApiRef,\n formatEntityRefTitle,\n} from '@backstage/plugin-catalog-react';\nimport { Box, FormHelperText, Grid, Typography } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport React, { useCallback, useEffect, useState } from 'react';\nimport { UnpackNestedValue, UseFormReturn } from 'react-hook-form';\nimport { useAsync } from 'react-use';\nimport YAML from 'yaml';\nimport { AnalyzeResult, catalogImportApiRef } from '../../api';\nimport { PartialEntity } from '../../types';\nimport { BackButton, NextButton } from '../Buttons';\nimport { PrepareResult } from '../useImportState';\nimport { PreparePullRequestForm } from './PreparePullRequestForm';\nimport { PreviewCatalogInfoComponent } from './PreviewCatalogInfoComponent';\nimport { PreviewPullRequestComponent } from './PreviewPullRequestComponent';\n\nconst useStyles = makeStyles(theme => ({\n previewCard: {\n marginTop: theme.spacing(1),\n },\n previewCardContent: {\n paddingTop: 0,\n },\n}));\n\ntype FormData = {\n title: string;\n body: string;\n componentName: string;\n owner: string;\n useCodeowners: boolean;\n};\n\ntype Props = {\n analyzeResult: Extract<AnalyzeResult, { type: 'repository' }>;\n onPrepare: (\n result: PrepareResult,\n opts?: { notRepeatable?: boolean },\n ) => void;\n onGoBack?: () => void;\n\n renderFormFields: (\n props: Pick<\n UseFormReturn<FormData>,\n 'register' | 'setValue' | 'formState'\n > & {\n values: UnpackNestedValue<FormData>;\n groups: string[];\n groupsLoading: boolean;\n },\n ) => React.ReactNode;\n};\n\nexport function generateEntities(\n entities: PartialEntity[],\n componentName: string,\n owner?: string,\n): Entity[] {\n return entities.map(e => ({\n ...e,\n apiVersion: e.apiVersion!,\n kind: e.kind!,\n metadata: {\n ...e.metadata,\n name: componentName,\n },\n spec: {\n ...e.spec,\n ...(owner ? { owner } : {}),\n },\n }));\n}\n\nexport const StepPrepareCreatePullRequest = ({\n analyzeResult,\n onPrepare,\n onGoBack,\n renderFormFields,\n}: Props) => {\n const classes = useStyles();\n const catalogApi = useApi(catalogApiRef);\n const catalogImportApi = useApi(catalogImportApiRef);\n const errorApi = useApi(errorApiRef);\n\n const [submitted, setSubmitted] = useState(false);\n const [error, setError] = useState<string>();\n\n const {\n loading: prDefaultsLoading,\n value: prDefaults,\n error: prDefaultsError,\n } = useAsync(\n () => catalogImportApi.preparePullRequest!(),\n [catalogImportApi.preparePullRequest],\n );\n\n useEffect(() => {\n if (prDefaultsError) {\n errorApi.post(prDefaultsError);\n }\n }, [prDefaultsError, errorApi]);\n\n const { loading: groupsLoading, value: groups } = useAsync(async () => {\n const groupEntities = await catalogApi.getEntities({\n filter: { kind: 'group' },\n });\n\n return groupEntities.items\n .map(e => formatEntityRefTitle(e, { defaultKind: 'group' }))\n .sort();\n });\n\n const handleResult = useCallback(\n async (data: FormData) => {\n setSubmitted(true);\n\n try {\n const pr = await catalogImportApi.submitPullRequest({\n repositoryUrl: analyzeResult.url,\n title: data.title,\n body: data.body,\n fileContent: generateEntities(\n analyzeResult.generatedEntities,\n data.componentName,\n data.owner,\n )\n .map(e => YAML.stringify(e))\n .join('---\\n'),\n });\n\n onPrepare(\n {\n type: 'repository',\n url: analyzeResult.url,\n integrationType: analyzeResult.integrationType,\n pullRequest: {\n url: pr.link,\n },\n locations: [\n {\n target: pr.location,\n entities: generateEntities(\n analyzeResult.generatedEntities,\n data.componentName,\n data.owner,\n ).map(e => ({\n kind: e.kind,\n namespace: e.metadata.namespace!,\n name: e.metadata.name,\n })),\n },\n ],\n },\n { notRepeatable: true },\n );\n } catch (e) {\n assertError(e);\n setError(e.message);\n setSubmitted(false);\n }\n },\n [\n analyzeResult.generatedEntities,\n analyzeResult.integrationType,\n analyzeResult.url,\n catalogImportApi,\n onPrepare,\n ],\n );\n\n return (\n <>\n <Typography>\n You entered a link to a {analyzeResult.integrationType} repository but a{' '}\n <code>catalog-info.yaml</code> could not be found. Use this form to open\n a Pull Request that creates one.\n </Typography>\n\n {!prDefaultsLoading && (\n <PreparePullRequestForm<FormData>\n onSubmit={handleResult}\n defaultValues={{\n title: prDefaults?.title ?? '',\n body: prDefaults?.body ?? '',\n owner:\n (analyzeResult.generatedEntities[0]?.spec?.owner as string) || '',\n componentName:\n analyzeResult.generatedEntities[0]?.metadata?.name || '',\n useCodeowners: false,\n }}\n render={({ values, formState, register, setValue }) => (\n <>\n {renderFormFields({\n values,\n formState,\n register,\n setValue,\n groups: groups ?? [],\n groupsLoading,\n })}\n\n <Box marginTop={2}>\n <Typography variant=\"h6\">Preview Pull Request</Typography>\n </Box>\n\n <PreviewPullRequestComponent\n title={values.title}\n description={values.body}\n classes={{\n card: classes.previewCard,\n cardContent: classes.previewCardContent,\n }}\n />\n\n <Box marginTop={2} marginBottom={1}>\n <Typography variant=\"h6\">Preview Entities</Typography>\n </Box>\n\n <PreviewCatalogInfoComponent\n entities={generateEntities(\n analyzeResult.generatedEntities,\n values.componentName,\n values.owner,\n )}\n repositoryUrl={analyzeResult.url}\n classes={{\n card: classes.previewCard,\n cardContent: classes.previewCardContent,\n }}\n />\n\n {error && <FormHelperText error>{error}</FormHelperText>}\n\n <Grid container spacing={0}>\n {onGoBack && (\n <BackButton onClick={onGoBack} disabled={submitted} />\n )}\n <NextButton\n type=\"submit\"\n disabled={Boolean(\n formState.errors.title ||\n formState.errors.body ||\n formState.errors.owner,\n )}\n loading={submitted}\n >\n Create PR\n </NextButton>\n </Grid>\n </>\n )}\n />\n )}\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Checkbox,\n Grid,\n ListItem,\n ListItemIcon,\n ListItemText,\n Typography,\n} from '@material-ui/core';\nimport LocationOnIcon from '@material-ui/icons/LocationOn';\nimport React, { useCallback, useState } from 'react';\nimport { AnalyzeResult } from '../../api';\nimport { BackButton, NextButton } from '../Buttons';\nimport { EntityListComponent } from '../EntityListComponent';\nimport { PrepareResult } from '../useImportState';\nimport partition from 'lodash/partition';\n\ntype Props = {\n analyzeResult: Extract<AnalyzeResult, { type: 'locations' }>;\n prepareResult?: PrepareResult;\n onPrepare: (result: PrepareResult) => void;\n onGoBack?: () => void;\n};\n\n/**\n * A form that lets a user select one of a list of locations to import\n *\n * @param analyzeResult the result of the analysis\n * @param prepareResult the selectected locations from a previous step\n * @param onPrepare called after the selection\n * @param onGoBack called to go back to the previous step\n */\nexport const StepPrepareSelectLocations = ({\n analyzeResult,\n prepareResult,\n onPrepare,\n onGoBack,\n}: Props) => {\n const [selectedUrls, setSelectedUrls] = useState<string[]>(\n prepareResult?.locations.map(l => l.target) || [],\n );\n\n const [existingLocations, locations] = partition(\n analyzeResult?.locations,\n l => l.exists,\n );\n\n const handleResult = useCallback(async () => {\n onPrepare({\n type: 'locations',\n locations: locations.filter((l: any) => selectedUrls.includes(l.target)),\n });\n }, [locations, onPrepare, selectedUrls]);\n\n const onItemClick = (url: string) => {\n setSelectedUrls(urls =>\n urls.includes(url) ? urls.filter(u => u !== url) : urls.concat(url),\n );\n };\n\n const onSelectAll = () => {\n setSelectedUrls(urls =>\n urls.length < locations.length ? locations.map(l => l.target) : [],\n );\n };\n\n return (\n <>\n {locations.length > 0 && (\n <>\n <Typography>\n Select one or more locations that are present in your git\n repository:\n </Typography>\n <EntityListComponent\n firstListItem={\n <ListItem dense button onClick={onSelectAll}>\n <ListItemIcon>\n <Checkbox\n edge=\"start\"\n checked={selectedUrls.length === locations.length}\n indeterminate={\n selectedUrls.length > 0 &&\n selectedUrls.length < locations.length\n }\n tabIndex={-1}\n disableRipple\n />\n </ListItemIcon>\n <ListItemText primary=\"Select All\" />\n </ListItem>\n }\n onItemClick={onItemClick}\n locations={locations}\n locationListItemIcon={target => (\n <Checkbox\n edge=\"start\"\n checked={selectedUrls.includes(target)}\n tabIndex={-1}\n disableRipple\n />\n )}\n collapsed\n />\n </>\n )}\n\n {existingLocations.length > 0 && (\n <>\n <Typography>These locations already exist in the catalog:</Typography>\n <EntityListComponent\n locations={existingLocations}\n locationListItemIcon={() => <LocationOnIcon />}\n withLinks\n collapsed\n />\n </>\n )}\n\n <Grid container spacing={0}>\n {onGoBack && <BackButton onClick={onGoBack} />}\n <NextButton disabled={selectedUrls.length === 0} onClick={handleResult}>\n Review\n </NextButton>\n </Grid>\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\nimport { FormHelperText, Grid, Typography } from '@material-ui/core';\nimport LocationOnIcon from '@material-ui/icons/LocationOn';\nimport React, { useCallback, useState } from 'react';\nimport { BackButton, NextButton } from '../Buttons';\nimport { EntityListComponent } from '../EntityListComponent';\nimport { PrepareResult, ReviewResult } from '../useImportState';\n\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Link } from '@backstage/core-components';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { assertError } from '@backstage/errors';\n\ntype Props = {\n prepareResult: PrepareResult;\n onReview: (result: ReviewResult) => void;\n onGoBack?: () => void;\n};\n\nexport const StepReviewLocation = ({\n prepareResult,\n onReview,\n onGoBack,\n}: Props) => {\n const catalogApi = useApi(catalogApiRef);\n const configApi = useApi(configApiRef);\n\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n\n const [submitted, setSubmitted] = useState(false);\n const [error, setError] = useState<string>();\n const exists =\n prepareResult.type === 'locations' &&\n prepareResult.locations.some(l => l.exists)\n ? true\n : false;\n const handleClick = useCallback(async () => {\n setSubmitted(true);\n try {\n let refreshed = new Array<{ target: string }>();\n if (prepareResult.type === 'locations') {\n refreshed = await Promise.all(\n prepareResult.locations\n .filter(l => l.exists)\n .map(async l => {\n const ref = stringifyEntityRef(l.entities[0] ?? l);\n await catalogApi.refreshEntity(ref);\n return { target: l.target };\n }),\n );\n }\n\n const locations = await Promise.all(\n prepareResult.locations\n .filter((l: unknown) => !(l as { exists?: boolean }).exists)\n .map(async l => {\n const result = await catalogApi.addLocation({\n type: 'url',\n target: l.target,\n presence:\n prepareResult.type === 'repository' ? 'optional' : 'required',\n });\n return {\n target: result.location.target,\n entities: result.entities,\n };\n }),\n );\n\n onReview({\n ...prepareResult,\n ...{ refreshed },\n locations,\n });\n } catch (e) {\n assertError(e);\n // TODO: this error should be handled differently. We add it as 'optional' and\n // it is not uncommon that a PR has not been merged yet.\n if (\n prepareResult.type === 'repository' &&\n e.message.startsWith(\n 'Location was added but has no entities specified yet',\n )\n ) {\n onReview({\n ...prepareResult,\n locations: prepareResult.locations.map(l => ({\n target: l.target,\n entities: [],\n })),\n });\n } else {\n setError(e.message);\n setSubmitted(false);\n }\n }\n }, [prepareResult, onReview, catalogApi]);\n\n return (\n <>\n {prepareResult.type === 'repository' && (\n <>\n <Typography paragraph>\n The following Pull Request has been opened:{' '}\n <Link\n to={prepareResult.pullRequest.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {prepareResult.pullRequest.url}\n </Link>\n </Typography>\n\n <Typography paragraph>\n You can already import the location and {appTitle} will fetch the\n entities as soon as the Pull Request is merged.\n </Typography>\n </>\n )}\n\n <Typography>\n {exists\n ? 'The following locations already exist in the catalog:'\n : 'The following entities will be added to the catalog:'}\n </Typography>\n\n <EntityListComponent\n locations={prepareResult.locations}\n locationListItemIcon={() => <LocationOnIcon />}\n />\n\n {error && <FormHelperText error>{error}</FormHelperText>}\n\n <Grid container spacing={0}>\n {onGoBack && <BackButton onClick={onGoBack} disabled={submitted} />}\n <NextButton\n disabled={submitted}\n loading={submitted}\n onClick={() => handleClick()}\n >\n {exists ? 'Refresh' : 'Import'}\n </NextButton>\n </Grid>\n </>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n FormHelperText,\n StepLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport React from 'react';\nimport { BackButton } from '../Buttons';\nimport { asInputRef } from '../helpers';\nimport { StepFinishImportLocation } from '../StepFinishImportLocation';\nimport { StepInitAnalyzeUrl } from '../StepInitAnalyzeUrl';\nimport {\n AutocompleteTextField,\n StepPrepareCreatePullRequest,\n} from '../StepPrepareCreatePullRequest';\nimport { StepPrepareSelectLocations } from '../StepPrepareSelectLocations';\nimport { StepReviewLocation } from '../StepReviewLocation';\nimport { StepperApis } from '../types';\nimport { ImportFlows, ImportState } from '../useImportState';\n\nexport type StepConfiguration = {\n stepLabel: React.ReactElement;\n content: React.ReactElement;\n};\n\nexport type StepperProvider = {\n analyze: (\n s: Extract<ImportState, { activeState: 'analyze' }>,\n opts: { apis: StepperApis },\n ) => StepConfiguration;\n prepare: (\n s: Extract<ImportState, { activeState: 'prepare' }>,\n opts: { apis: StepperApis },\n ) => StepConfiguration;\n review: (\n s: Extract<ImportState, { activeState: 'review' }>,\n opts: { apis: StepperApis },\n ) => StepConfiguration;\n finish: (\n s: Extract<ImportState, { activeState: 'finish' }>,\n opts: { apis: StepperApis },\n ) => StepConfiguration;\n};\n\n/**\n * The default stepper generation function.\n *\n * Override this function to customize the import flow. Each flow should at\n * least override the prepare operation.\n *\n * @param flow the name of the active flow\n * @param defaults the default steps\n */\nexport function defaultGenerateStepper(\n flow: ImportFlows,\n defaults: StepperProvider,\n): StepperProvider {\n switch (flow) {\n // the prepare step is skipped but the label of the step is updated\n case 'single-location':\n return {\n ...defaults,\n prepare: () => ({\n stepLabel: (\n <StepLabel\n optional={\n <Typography variant=\"caption\">\n Discovered Locations: 1\n </Typography>\n }\n >\n Select Locations\n </StepLabel>\n ),\n content: <></>,\n }),\n };\n\n // let the user select one or more of the discovered locations in the prepare step\n case 'multiple-locations':\n return {\n ...defaults,\n prepare: (state, opts) => {\n if (state.analyzeResult.type !== 'locations') {\n return defaults.prepare(state, opts);\n }\n\n return {\n stepLabel: (\n <StepLabel\n optional={\n <Typography variant=\"caption\">\n Discovered Locations: {state.analyzeResult.locations.length}\n </Typography>\n }\n >\n Select Locations\n </StepLabel>\n ),\n content: (\n <StepPrepareSelectLocations\n analyzeResult={state.analyzeResult}\n prepareResult={state.prepareResult}\n onPrepare={state.onPrepare}\n onGoBack={state.onGoBack}\n />\n ),\n };\n },\n };\n\n case 'no-location':\n return {\n ...defaults,\n prepare: (state, opts) => {\n if (state.analyzeResult.type !== 'repository') {\n return defaults.prepare(state, opts);\n }\n\n return {\n stepLabel: <StepLabel>Create Pull Request</StepLabel>,\n content: (\n <StepPrepareCreatePullRequest\n analyzeResult={state.analyzeResult}\n onPrepare={state.onPrepare}\n onGoBack={state.onGoBack}\n renderFormFields={({\n values,\n setValue,\n formState,\n groupsLoading,\n groups,\n register,\n }) => (\n <>\n <Box marginTop={2}>\n <Typography variant=\"h6\">Pull Request Details</Typography>\n </Box>\n\n <TextField\n {...asInputRef(\n register('title', {\n required: true,\n }),\n )}\n label=\"Pull Request Title\"\n placeholder=\"Add Backstage catalog entity descriptor files\"\n margin=\"normal\"\n variant=\"outlined\"\n fullWidth\n error={Boolean(formState.errors.title)}\n required\n />\n\n <TextField\n {...asInputRef(\n register('body', {\n required: true,\n }),\n )}\n label=\"Pull Request Body\"\n placeholder=\"A describing text with Markdown support\"\n margin=\"normal\"\n variant=\"outlined\"\n fullWidth\n error={Boolean(formState.errors.body)}\n multiline\n required\n />\n\n <Box marginTop={2}>\n <Typography variant=\"h6\">Entity Configuration</Typography>\n </Box>\n\n <TextField\n {...asInputRef(\n register('componentName', { required: true }),\n )}\n label=\"Name of the created component\"\n placeholder=\"my-component\"\n margin=\"normal\"\n variant=\"outlined\"\n fullWidth\n error={Boolean(formState.errors.componentName)}\n required\n />\n\n {!values.useCodeowners && (\n <AutocompleteTextField\n name=\"owner\"\n errors={formState.errors}\n options={groups || []}\n loading={groupsLoading}\n loadingText=\"Loading groups…\"\n helperText=\"Select an owner from the list or enter a reference to a Group or a User\"\n errorHelperText=\"required value\"\n textFieldProps={{\n label: 'Entity Owner',\n placeholder: 'my-group',\n }}\n rules={{ required: true }}\n required\n />\n )}\n\n <FormControlLabel\n control={\n <Checkbox\n {...asInputRef(register('useCodeowners'))}\n onChange={(_, value) => {\n if (value) {\n setValue('owner', '');\n }\n }}\n />\n }\n label={\n <>\n Use <em>CODEOWNERS</em> file as Entity Owner\n </>\n }\n />\n <FormHelperText>\n WARNING: This may fail if no CODEOWNERS file is found at\n the target location.\n </FormHelperText>\n </>\n )}\n />\n ),\n };\n },\n };\n\n default:\n return defaults;\n }\n}\n\nexport const defaultStepper: StepperProvider = {\n analyze: (state, { apis }) => ({\n stepLabel: <StepLabel>Select URL</StepLabel>,\n content: (\n <StepInitAnalyzeUrl\n key=\"analyze\"\n analysisUrl={state.analysisUrl}\n onAnalysis={state.onAnalysis}\n disablePullRequest={!apis.catalogImportApi.preparePullRequest}\n />\n ),\n }),\n\n prepare: state => ({\n stepLabel: (\n <StepLabel optional={<Typography variant=\"caption\">Optional</Typography>}>\n Import Actions\n </StepLabel>\n ),\n content: <BackButton onClick={state.onGoBack} />,\n }),\n\n review: state => ({\n stepLabel: <StepLabel>Review</StepLabel>,\n content: (\n <StepReviewLocation\n prepareResult={state.prepareResult}\n onReview={state.onReview}\n onGoBack={state.onGoBack}\n />\n ),\n }),\n\n finish: state => ({\n stepLabel: <StepLabel>Finish</StepLabel>,\n content: (\n <StepFinishImportLocation\n prepareResult={state.prepareResult}\n onReset={state.onReset}\n />\n ),\n }),\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InfoCard, InfoCardVariants } from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Step, StepContent, Stepper } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport React, { useMemo } from 'react';\nimport { catalogImportApiRef } from '../../api';\nimport { ImportFlows, ImportState, useImportState } from '../useImportState';\nimport {\n defaultGenerateStepper,\n defaultStepper,\n StepConfiguration,\n StepperProvider,\n} from './defaults';\n\nconst useStyles = makeStyles(() => ({\n stepperRoot: {\n padding: 0,\n },\n}));\n\ntype Props = {\n initialUrl?: string;\n generateStepper?: (\n flow: ImportFlows,\n defaults: StepperProvider,\n ) => StepperProvider;\n variant?: InfoCardVariants;\n};\n\nexport const ImportStepper = ({\n initialUrl,\n generateStepper = defaultGenerateStepper,\n variant,\n}: Props) => {\n const catalogImportApi = useApi(catalogImportApiRef);\n const classes = useStyles();\n const state = useImportState({ initialUrl });\n\n const states = useMemo<StepperProvider>(\n () => generateStepper(state.activeFlow, defaultStepper),\n [generateStepper, state.activeFlow],\n );\n\n const render = (step: StepConfiguration) => {\n return (\n <Step>\n {step.stepLabel}\n <StepContent>{step.content}</StepContent>\n </Step>\n );\n };\n\n return (\n <InfoCard variant={variant}>\n <Stepper\n classes={{ root: classes.stepperRoot }}\n activeStep={state.activeStepNumber}\n orientation=\"vertical\"\n >\n {render(\n states.analyze(\n state as Extract<ImportState, { activeState: 'analyze' }>,\n { apis: { catalogImportApi } },\n ),\n )}\n {render(\n states.prepare(\n state as Extract<ImportState, { activeState: 'prepare' }>,\n { apis: { catalogImportApi } },\n ),\n )}\n {render(\n states.review(\n state as Extract<ImportState, { activeState: 'review' }>,\n { apis: { catalogImportApi } },\n ),\n )}\n {render(\n states.finish(\n state as Extract<ImportState, { activeState: 'finish' }>,\n { apis: { catalogImportApi } },\n ),\n )}\n </Stepper>\n </InfoCard>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Content,\n ContentHeader,\n Header,\n Page,\n SupportButton,\n} from '@backstage/core-components';\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Grid } from '@material-ui/core';\nimport React from 'react';\nimport { ImportInfoCard } from '../ImportInfoCard';\nimport { ImportStepper } from '../ImportStepper';\n\nexport const DefaultImportPage = () => {\n const configApi = useApi(configApiRef);\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n\n return (\n <Page themeId=\"home\">\n <Header title=\"Register an existing component\" />\n <Content>\n <ContentHeader title={`Start tracking your component in ${appTitle}`}>\n <SupportButton>\n Start tracking your component in {appTitle} by adding it to the\n software catalog.\n </SupportButton>\n </ContentHeader>\n\n <Grid container spacing={2} direction=\"row-reverse\">\n <Grid item xs={12} md={4} lg={6} xl={8}>\n <ImportInfoCard />\n </Grid>\n\n <Grid item xs={12} md={8} lg={6} xl={4}>\n <ImportStepper />\n </Grid>\n </Grid>\n </Content>\n </Page>\n );\n};\n"],"names":["useStyles","this"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-import",
3
3
  "description": "A Backstage plugin the helps you import entities into your catalog",
4
- "version": "0.7.2",
4
+ "version": "0.7.6",
5
5
  "main": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -31,43 +31,45 @@
31
31
  "clean": "backstage-cli clean"
32
32
  },
33
33
  "dependencies": {
34
- "@backstage/catalog-client": "^0.5.0",
35
- "@backstage/catalog-model": "^0.9.4",
36
- "@backstage/core-components": "^0.7.0",
37
- "@backstage/core-plugin-api": "^0.1.10",
38
- "@backstage/integration": "^0.6.8",
39
- "@backstage/integration-react": "^0.1.12",
40
- "@backstage/plugin-catalog-react": "^0.6.0",
34
+ "@backstage/catalog-client": "^0.5.3",
35
+ "@backstage/catalog-model": "^0.9.7",
36
+ "@backstage/core-components": "^0.8.2",
37
+ "@backstage/core-plugin-api": "^0.4.0",
38
+ "@backstage/errors": "^0.1.3",
39
+ "@backstage/integration": "^0.6.10",
40
+ "@backstage/integration-react": "^0.1.16",
41
+ "@backstage/plugin-catalog-react": "^0.6.8",
41
42
  "@material-ui/core": "^4.12.2",
42
43
  "@material-ui/icons": "^4.9.1",
43
44
  "@material-ui/lab": "4.0.0-alpha.57",
44
45
  "@octokit/rest": "^18.5.3",
45
- "@types/react": "*",
46
46
  "git-url-parse": "^11.6.0",
47
47
  "js-base64": "^3.6.0",
48
48
  "lodash": "^4.17.21",
49
- "react": "^16.13.1",
50
- "react-dom": "^16.13.1",
51
49
  "react-hook-form": "^7.12.2",
52
50
  "react-router": "6.0.0-beta.0",
53
51
  "react-use": "^17.2.4",
54
52
  "yaml": "^1.10.0"
55
53
  },
54
+ "peerDependencies": {
55
+ "@types/react": "^16.13.1 || ^17.0.0",
56
+ "react": "^16.13.1 || ^17.0.0"
57
+ },
56
58
  "devDependencies": {
57
- "@backstage/cli": "^0.7.16",
58
- "@backstage/core-app-api": "^0.1.17",
59
- "@backstage/dev-utils": "^0.2.12",
60
- "@backstage/test-utils": "^0.1.19",
59
+ "@backstage/cli": "^0.10.3",
60
+ "@backstage/core-app-api": "^0.3.0",
61
+ "@backstage/dev-utils": "^0.2.15",
62
+ "@backstage/test-utils": "^0.2.0",
61
63
  "@testing-library/jest-dom": "^5.10.1",
62
64
  "@testing-library/react": "^11.2.5",
63
65
  "@testing-library/react-hooks": "^7.0.2",
64
66
  "@testing-library/user-event": "^13.1.8",
65
67
  "@types/jest": "^26.0.7",
66
68
  "cross-fetch": "^3.0.6",
67
- "msw": "^0.29.0"
69
+ "msw": "^0.35.0"
68
70
  },
69
71
  "files": [
70
72
  "dist"
71
73
  ],
72
- "gitHead": "1b02df9f467ea11a4571df46faabe655c3ee10c8"
74
+ "gitHead": "b315430f9dfcfa19ab0dd90f5b4ac6904938fba7"
73
75
  }