@spinnaker/core 0.11.4 → 0.12.0
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 +53 -0
- package/dist/cloudProvider/CloudProviderLogo.d.ts +0 -5
- package/dist/config/settings.d.ts +1 -0
- package/dist/core.module.d.ts +1 -1
- package/dist/index.js +40 -32
- package/dist/index.js.map +1 -1
- package/dist/managed/constraints/registry.d.ts +1 -1
- package/dist/managed/environmentBaseElements/EnvironmentsRender.d.ts +3 -2
- package/dist/managed/graphql/graphql-sdk.d.ts +35 -0
- package/dist/managed/overview/PreviewEnvironments.d.ts +8 -0
- package/dist/managed/resources/ToggleResourceManagement.d.ts +9 -0
- package/dist/managed/resources/resourceRegistry.d.ts +1 -1
- package/dist/managed/utils/defaults.d.ts +1 -1
- package/dist/managed/utils/useNotifyOnError.hook.d.ts +6 -0
- package/dist/pipeline/service/execution.service.d.ts +2 -1
- package/package.json +6 -6
- package/src/cloudProvider/CloudProviderLogo.tsx +0 -5
- package/src/cloudProvider/CloudProviderRegistry.ts +6 -65
- package/src/cloudProvider/providerSelection/ProviderSelectionService.spec.ts +96 -0
- package/src/cloudProvider/providerSelection/ProviderSelectionService.ts +6 -8
- package/src/cluster/allClusters.html +1 -0
- package/src/config/settings.ts +2 -0
- package/src/core.module.ts +1 -1
- package/src/function/function.dataSource.ts +1 -0
- package/src/instance/details/multipleInstances.controller.js +6 -0
- package/src/instance/details/multipleInstances.view.html +1 -0
- package/src/managed/config/Configuration.tsx +8 -1
- package/src/managed/config/DeliveryConfig.tsx +3 -9
- package/src/managed/config/GitIntegration.tsx +8 -1
- package/src/managed/environmentBaseElements/EnvironmentsRender.tsx +7 -1
- package/src/managed/externals/toggleResourceManagement.tsx +12 -59
- package/src/managed/graphql/graphql-sdk.ts +54 -0
- package/src/managed/graphql/schema.graphql +1 -0
- package/src/managed/overview/EnvironmentsOverview.tsx +8 -11
- package/src/managed/overview/PreviewEnvironments.tsx +47 -0
- package/src/managed/overview/Resource.tsx +76 -34
- package/src/managed/overview/artifact/ArtifactVersionTasks.tsx +3 -10
- package/src/managed/overview/artifact/Constraints.tsx +3 -11
- package/src/managed/overview/queries.graphql +8 -0
- package/src/managed/resources/ToggleResourceManagement.tsx +53 -0
- package/src/managed/utils/defaults.ts +1 -1
- package/src/managed/utils/useNotifyOnError.hook.ts +22 -0
- package/src/modal/modals.less +1 -1
- package/src/pipeline/details/SingleExecutionDetails.tsx +1 -1
- package/src/pipeline/service/execution.service.ts +7 -2
- package/src/presentation/main.less +3 -3
- package/src/serverGroup/details/multipleServerGroups.controller.js +6 -0
- package/src/serverGroup/details/multipleServerGroups.view.html +1 -0
- package/src/utils/clipboard/CopyToClipboard.tsx +1 -1
|
@@ -38,7 +38,7 @@ export interface IConstraintHandler<K = string> {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
declare class ConstraintsManager extends BasePluginManager<IConstraintHandler> {
|
|
41
|
-
getIcon(constraint: IConstraint | IBaseConstraint): "placeholder" | "menu" | "add" | "environment" | "config" | "artifact" | "close" | "play" | "plus" | "accordionCollapse" | "accordionExpand" | "accordionExpandAll" | "artifactApproved" | "artifactBad" | "artifactPending" | "artifactSkipped" | "bake" | "build" | "buildFail" | "buildSuccess" | "canaryConfig" | "canaryFail" | "canaryRunning" | "canaryPass" | "canaryMarginal" | "caretRight" | "checkBadge" | "checkboxIndeterminate" | "checkboxChecked" | "checkboxUnchecked" | "closeSmall" | "cloudDeployed" | "cloudError" | "cloudProgress" | "cloudDecommissioned" | "cloudWaiting" | "cluster" | "configJ" | "configM" | "configS" | "copyClipboard" | "done" | "duplicate" | "edit" | "fileJson" | "fn" | "fnNew" | "formDrag" | "formError" | "formInfo" | "formNetworkBad" | "formNetworkGood" | "formRefresh" | "formWarning" | "heart" | "history" | "instances" | "loadBalancer" | "manualJudgement" | "manualJudgementApproved" | "manualJudgementRejected" | "mdActuating" | "mdActuationLaunched" | "mdCreated" | "mdDelay" | "mdDeltaDetected" | "mdDeltaResolved" | "mdDiff" | "mdError" | "mdUnhappy" | "mdPaused" | "mdResumed" | "mdUnknown" | "mdConstraintGeneric" | "mdConstraintDependsOn" | "mdConstraintAllowedTimes" | "md" | "mdVerification" | "menuClose" | "minus" | "pin" | "resourceT" | "securityGroup" | "servergroupAws" | "spCIBranch" | "spCIBuild" | "spCICommit" | "spCIMaster" | "spCIMerged" | "spCIPullRequest" | "spCIPullRequestClosed" | "spEnvironments" | "spMenuAppInSync" | "spMenuAppUnsynced" | "spMenuCanaryConfig" | "spMenuCanaryReport" | "spMenuClusters" | "spMenuConfig" | "spMenuK8s" | "spMenuLoadBalancers" | "spMenuMeme" | "spMenuPager" | "spMenuPipelines" | "spMenuProperties" | "spMenuSecurityGroups" | "spMenuTasks" | "spMenuTimeline" | "spMenuZuul" | "spel" | "templateFull" | "templateS" | "templateWorkflow" | "toggleOff" | "toggleOn" | "trash" | "unpin";
|
|
41
|
+
getIcon(constraint: IConstraint | IBaseConstraint): "placeholder" | "menu" | "add" | "environment" | "config" | "artifact" | "close" | "play" | "plus" | "accordionCollapse" | "accordionExpand" | "accordionExpandAll" | "artifactApproved" | "artifactBad" | "artifactPending" | "artifactSkipped" | "bake" | "build" | "buildFail" | "buildSuccess" | "canaryConfig" | "canaryFail" | "canaryRunning" | "canaryPass" | "canaryMarginal" | "caretRight" | "checkBadge" | "checkboxIndeterminate" | "checkboxChecked" | "checkboxUnchecked" | "closeSmall" | "cloudDeployed" | "cloudError" | "cloudProgress" | "cloudDecommissioned" | "cloudWaiting" | "cluster" | "configJ" | "configM" | "configS" | "copyClipboard" | "done" | "duplicate" | "edit" | "fileJson" | "fn" | "fnNew" | "formDrag" | "formError" | "formInfo" | "formNetworkBad" | "formNetworkGood" | "formRefresh" | "formWarning" | "heart" | "history" | "instances" | "loadBalancer" | "manualJudgement" | "manualJudgementApproved" | "manualJudgementRejected" | "mdActuating" | "mdActuationLaunched" | "mdCreated" | "mdDelay" | "mdDeltaDetected" | "mdDeltaResolved" | "mdDiff" | "mdError" | "mdUnhappy" | "mdPaused" | "mdResumed" | "mdUnknown" | "mdConstraintGeneric" | "mdConstraintDependsOn" | "mdConstraintAllowedTimes" | "md" | "mdVerification" | "menuClose" | "minus" | "pin" | "resourceT" | "securityGroup" | "servergroupAws" | "spCIBranch" | "spCIBuild" | "spCICommit" | "spCIMaster" | "spCIMerged" | "spCIPullRequest" | "spCIPullRequestClosed" | "spEnvironments" | "spMenuAppInSync" | "spMenuAppUnsynced" | "spMenuCanaryConfig" | "spMenuCanaryReport" | "spMenuClusters" | "spMenuConfig" | "spMenuFunctions" | "spMenuK8s" | "spMenuLoadBalancers" | "spMenuMeme" | "spMenuPager" | "spMenuPipelines" | "spMenuProperties" | "spMenuSecurityGroups" | "spMenuTasks" | "spMenuTimeline" | "spMenuZuul" | "spel" | "templateFull" | "templateS" | "templateWorkflow" | "toggleOff" | "toggleOn" | "trash" | "unpin";
|
|
42
42
|
renderTitle(constraint: IConstraint): React.ReactNode;
|
|
43
43
|
hasContent(constraint: IConstraint): boolean;
|
|
44
44
|
renderDescription(constraint: IConstraint): React.ReactNode;
|
|
@@ -10,10 +10,11 @@ interface IEnvironmentsRenderProps {
|
|
|
10
10
|
style?: React.CSSProperties;
|
|
11
11
|
children?: React.ReactNode;
|
|
12
12
|
}
|
|
13
|
-
export
|
|
13
|
+
export interface OrderedEnvironments<T> {
|
|
14
14
|
className?: string;
|
|
15
15
|
style?: React.CSSProperties;
|
|
16
16
|
environments: T[];
|
|
17
|
-
}
|
|
17
|
+
}
|
|
18
|
+
export declare const useOrderedEnvironment: <T extends object>(ref: React.RefObject<HTMLDivElement>, environments: T[]) => OrderedEnvironments<T>;
|
|
18
19
|
export declare const EnvironmentsRender: React.ForwardRefExoticComponent<IEnvironmentsRenderProps & React.RefAttributes<HTMLDivElement>>;
|
|
19
20
|
export {};
|
|
@@ -106,6 +106,7 @@ export interface MdConfig {
|
|
|
106
106
|
updatedAt?: Maybe<Scalars['InstantTime']>;
|
|
107
107
|
rawConfig?: Maybe<Scalars['String']>;
|
|
108
108
|
processedConfig?: Maybe<Scalars['String']>;
|
|
109
|
+
previewEnvironmentsConfigured?: Maybe<Scalars['Boolean']>;
|
|
109
110
|
}
|
|
110
111
|
export interface MdConstraint {
|
|
111
112
|
__typename?: 'MdConstraint';
|
|
@@ -467,6 +468,9 @@ export declare type FetchApplicationQuery = {
|
|
|
467
468
|
application?: Maybe<{
|
|
468
469
|
__typename?: 'MdApplication';
|
|
469
470
|
} & Pick<MdApplication, 'id' | 'name' | 'account'> & {
|
|
471
|
+
config?: Maybe<{
|
|
472
|
+
__typename?: 'MdConfig';
|
|
473
|
+
} & Pick<MdConfig, 'id' | 'previewEnvironmentsConfigured'>>;
|
|
470
474
|
environments: Array<{
|
|
471
475
|
__typename?: 'MdEnvironment';
|
|
472
476
|
} & Pick<MdEnvironment, 'isDeleting'> & {
|
|
@@ -721,6 +725,12 @@ export declare type ImportDeliveryConfigMutationVariables = Exact<{
|
|
|
721
725
|
export declare type ImportDeliveryConfigMutation = {
|
|
722
726
|
__typename?: 'Mutation';
|
|
723
727
|
} & Pick<Mutation, 'importDeliveryConfig'>;
|
|
728
|
+
export declare type ToggleResourceManagementMutationVariables = Exact<{
|
|
729
|
+
payload?: Maybe<MdToggleResourceManagementPayload>;
|
|
730
|
+
}>;
|
|
731
|
+
export declare type ToggleResourceManagementMutation = {
|
|
732
|
+
__typename?: 'Mutation';
|
|
733
|
+
} & Pick<Mutation, 'toggleResourceManagement'>;
|
|
724
734
|
export declare const ActionDetailsFragmentDoc: Apollo.DocumentNode;
|
|
725
735
|
export declare const DetailedVersionFieldsFragmentDoc: Apollo.DocumentNode;
|
|
726
736
|
export declare const ArtifactPinnedVersionFieldsFragmentDoc: Apollo.DocumentNode;
|
|
@@ -1194,3 +1204,28 @@ export declare function useImportDeliveryConfigMutation(baseOptions?: Apollo.Mut
|
|
|
1194
1204
|
export declare type ImportDeliveryConfigMutationHookResult = ReturnType<typeof useImportDeliveryConfigMutation>;
|
|
1195
1205
|
export declare type ImportDeliveryConfigMutationResult = Apollo.MutationResult<ImportDeliveryConfigMutation>;
|
|
1196
1206
|
export declare type ImportDeliveryConfigMutationOptions = Apollo.BaseMutationOptions<ImportDeliveryConfigMutation, ImportDeliveryConfigMutationVariables>;
|
|
1207
|
+
export declare const ToggleResourceManagementDocument: Apollo.DocumentNode;
|
|
1208
|
+
export declare type ToggleResourceManagementMutationFn = Apollo.MutationFunction<ToggleResourceManagementMutation, ToggleResourceManagementMutationVariables>;
|
|
1209
|
+
/**
|
|
1210
|
+
* __useToggleResourceManagementMutation__
|
|
1211
|
+
*
|
|
1212
|
+
* To run a mutation, you first call `useToggleResourceManagementMutation` within a React component and pass it any options that fit your needs.
|
|
1213
|
+
* When your component renders, `useToggleResourceManagementMutation` returns a tuple that includes:
|
|
1214
|
+
* - A mutate function that you can call at any time to execute the mutation
|
|
1215
|
+
* - An object with fields that represent the current status of the mutation's execution
|
|
1216
|
+
*
|
|
1217
|
+
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
|
1218
|
+
*
|
|
1219
|
+
* @example
|
|
1220
|
+
* const [toggleResourceManagementMutation, { data, loading, error }] = useToggleResourceManagementMutation({
|
|
1221
|
+
* variables: {
|
|
1222
|
+
* payload: // value for 'payload'
|
|
1223
|
+
* },
|
|
1224
|
+
* });
|
|
1225
|
+
*/
|
|
1226
|
+
export declare function useToggleResourceManagementMutation(baseOptions?: Apollo.MutationHookOptions<ToggleResourceManagementMutation, ToggleResourceManagementMutationVariables>): Apollo.MutationTuple<ToggleResourceManagementMutation, Exact<{
|
|
1227
|
+
payload?: MdToggleResourceManagementPayload;
|
|
1228
|
+
}>>;
|
|
1229
|
+
export declare type ToggleResourceManagementMutationHookResult = ReturnType<typeof useToggleResourceManagementMutation>;
|
|
1230
|
+
export declare type ToggleResourceManagementMutationResult = Apollo.MutationResult<ToggleResourceManagementMutation>;
|
|
1231
|
+
export declare type ToggleResourceManagementMutationOptions = Apollo.BaseMutationOptions<ToggleResourceManagementMutation, ToggleResourceManagementMutationVariables>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { OrderedEnvironments } from '../environmentBaseElements/EnvironmentsRender';
|
|
2
|
+
import { QueryEnvironment } from './types';
|
|
3
|
+
interface IPreviewEnvironmentsProps {
|
|
4
|
+
orderedEnvironments: OrderedEnvironments<QueryEnvironment>;
|
|
5
|
+
isConfigured?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const PreviewEnvironments: ({ orderedEnvironments, isConfigured }: IPreviewEnvironmentsProps) => JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface IToggleResourceManagementProps {
|
|
2
|
+
isPaused: boolean;
|
|
3
|
+
isActuating?: boolean;
|
|
4
|
+
regions: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare const ActuationWarning: () => JSX.Element;
|
|
7
|
+
export declare const ToggleResourceManagement: ({ isPaused, isActuating, regions }: IToggleResourceManagementProps) => JSX.Element;
|
|
8
|
+
export declare const MultiRegionWarning: ({ isPaused, regions }: Omit<IToggleResourceManagementProps, 'isActuating'>) => JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -13,7 +13,7 @@ export interface IResourceLinkProps {
|
|
|
13
13
|
displayName?: string;
|
|
14
14
|
}
|
|
15
15
|
declare class ResourcesManager extends BasePluginManager<IResourceKindConfig> {
|
|
16
|
-
getIcon(kind: string): "placeholder" | "menu" | "add" | "environment" | "config" | "artifact" | "close" | "play" | "plus" | "accordionCollapse" | "accordionExpand" | "accordionExpandAll" | "artifactApproved" | "artifactBad" | "artifactPending" | "artifactSkipped" | "bake" | "build" | "buildFail" | "buildSuccess" | "canaryConfig" | "canaryFail" | "canaryRunning" | "canaryPass" | "canaryMarginal" | "caretRight" | "checkBadge" | "checkboxIndeterminate" | "checkboxChecked" | "checkboxUnchecked" | "closeSmall" | "cloudDeployed" | "cloudError" | "cloudProgress" | "cloudDecommissioned" | "cloudWaiting" | "cluster" | "configJ" | "configM" | "configS" | "copyClipboard" | "done" | "duplicate" | "edit" | "fileJson" | "fn" | "fnNew" | "formDrag" | "formError" | "formInfo" | "formNetworkBad" | "formNetworkGood" | "formRefresh" | "formWarning" | "heart" | "history" | "instances" | "loadBalancer" | "manualJudgement" | "manualJudgementApproved" | "manualJudgementRejected" | "mdActuating" | "mdActuationLaunched" | "mdCreated" | "mdDelay" | "mdDeltaDetected" | "mdDeltaResolved" | "mdDiff" | "mdError" | "mdUnhappy" | "mdPaused" | "mdResumed" | "mdUnknown" | "mdConstraintGeneric" | "mdConstraintDependsOn" | "mdConstraintAllowedTimes" | "md" | "mdVerification" | "menuClose" | "minus" | "pin" | "resourceT" | "securityGroup" | "servergroupAws" | "spCIBranch" | "spCIBuild" | "spCICommit" | "spCIMaster" | "spCIMerged" | "spCIPullRequest" | "spCIPullRequestClosed" | "spEnvironments" | "spMenuAppInSync" | "spMenuAppUnsynced" | "spMenuCanaryConfig" | "spMenuCanaryReport" | "spMenuClusters" | "spMenuConfig" | "spMenuK8s" | "spMenuLoadBalancers" | "spMenuMeme" | "spMenuPager" | "spMenuPipelines" | "spMenuProperties" | "spMenuSecurityGroups" | "spMenuTasks" | "spMenuTimeline" | "spMenuZuul" | "spel" | "templateFull" | "templateS" | "templateWorkflow" | "toggleOff" | "toggleOn" | "trash" | "unpin";
|
|
16
|
+
getIcon(kind: string): "placeholder" | "menu" | "add" | "environment" | "config" | "artifact" | "close" | "play" | "plus" | "accordionCollapse" | "accordionExpand" | "accordionExpandAll" | "artifactApproved" | "artifactBad" | "artifactPending" | "artifactSkipped" | "bake" | "build" | "buildFail" | "buildSuccess" | "canaryConfig" | "canaryFail" | "canaryRunning" | "canaryPass" | "canaryMarginal" | "caretRight" | "checkBadge" | "checkboxIndeterminate" | "checkboxChecked" | "checkboxUnchecked" | "closeSmall" | "cloudDeployed" | "cloudError" | "cloudProgress" | "cloudDecommissioned" | "cloudWaiting" | "cluster" | "configJ" | "configM" | "configS" | "copyClipboard" | "done" | "duplicate" | "edit" | "fileJson" | "fn" | "fnNew" | "formDrag" | "formError" | "formInfo" | "formNetworkBad" | "formNetworkGood" | "formRefresh" | "formWarning" | "heart" | "history" | "instances" | "loadBalancer" | "manualJudgement" | "manualJudgementApproved" | "manualJudgementRejected" | "mdActuating" | "mdActuationLaunched" | "mdCreated" | "mdDelay" | "mdDeltaDetected" | "mdDeltaResolved" | "mdDiff" | "mdError" | "mdUnhappy" | "mdPaused" | "mdResumed" | "mdUnknown" | "mdConstraintGeneric" | "mdConstraintDependsOn" | "mdConstraintAllowedTimes" | "md" | "mdVerification" | "menuClose" | "minus" | "pin" | "resourceT" | "securityGroup" | "servergroupAws" | "spCIBranch" | "spCIBuild" | "spCICommit" | "spCIMaster" | "spCIMerged" | "spCIPullRequest" | "spCIPullRequestClosed" | "spEnvironments" | "spMenuAppInSync" | "spMenuAppUnsynced" | "spMenuCanaryConfig" | "spMenuCanaryReport" | "spMenuClusters" | "spMenuConfig" | "spMenuFunctions" | "spMenuK8s" | "spMenuLoadBalancers" | "spMenuMeme" | "spMenuPager" | "spMenuPipelines" | "spMenuProperties" | "spMenuSecurityGroups" | "spMenuTasks" | "spMenuTimeline" | "spMenuZuul" | "spel" | "templateFull" | "templateS" | "templateWorkflow" | "toggleOff" | "toggleOn" | "trash" | "unpin";
|
|
17
17
|
getExperimentalDisplayLink(resource: IResourceLinkProps): string | undefined;
|
|
18
18
|
getSpinnakerType(kind: string): string;
|
|
19
19
|
getNativeResourceRoutingInfo({ kind, account, stack, detail, displayName, }: IResourceLinkProps): {
|
|
@@ -8,4 +8,4 @@ export declare const MODAL_MAX_WIDTH = 750;
|
|
|
8
8
|
export declare const spinnerProps: ISpinnerProps;
|
|
9
9
|
export declare const ABSOLUTE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss ZZZZ";
|
|
10
10
|
export declare const MD_CATEGORY = "ManagedDelivery";
|
|
11
|
-
export declare const getDocsUrl: (doc: keyof IManagedDeliveryURLs) => string;
|
|
11
|
+
export declare const getDocsUrl: (doc: keyof IManagedDeliveryURLs) => string | undefined;
|
|
@@ -4,6 +4,7 @@ import { IQService, ITimeoutService } from 'angular';
|
|
|
4
4
|
import { Application } from '../../application/application.model';
|
|
5
5
|
import { ApplicationDataSource } from '../../application/service/applicationDataSource';
|
|
6
6
|
import { IExecution } from '../../domain';
|
|
7
|
+
import { IPipeline } from '../../domain/IPipeline';
|
|
7
8
|
import { IRetryablePromise } from '../../utils/retryablePromise';
|
|
8
9
|
export declare class ExecutionService {
|
|
9
10
|
private $q;
|
|
@@ -25,7 +26,7 @@ export declare class ExecutionService {
|
|
|
25
26
|
* @return {<IExecution[]>}
|
|
26
27
|
*/
|
|
27
28
|
getExecutions(applicationName: string, application?: Application, expand?: boolean): PromiseLike<IExecution[]>;
|
|
28
|
-
getExecution(executionId: string): PromiseLike<IExecution>;
|
|
29
|
+
getExecution(executionId: string, pipelineConfigs?: IPipeline[]): PromiseLike<IExecution>;
|
|
29
30
|
transformExecutions(application: Application, executions: IExecution[], currentData?: IExecution[]): void;
|
|
30
31
|
private getConfigIdsFromFilterModel;
|
|
31
32
|
private cleanExecutionForDiffing;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spinnaker/core",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"@apollo/client": "3.3.14",
|
|
18
18
|
"@fortawesome/fontawesome-free": "5.5.0",
|
|
19
19
|
"@spinnaker/mocks": "1.0.7",
|
|
20
|
-
"@spinnaker/presentation": "^0.0.
|
|
21
|
-
"@spinnaker/styleguide": "
|
|
20
|
+
"@spinnaker/presentation": "^0.0.12",
|
|
21
|
+
"@spinnaker/styleguide": "2.0.0",
|
|
22
22
|
"@uirouter/angularjs": "1.0.26",
|
|
23
23
|
"@uirouter/core": "6.0.8",
|
|
24
24
|
"@uirouter/react": "1.0.7",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"recoil": "0.0.10",
|
|
79
79
|
"rxjs": "6.6.7",
|
|
80
80
|
"select2-bootstrap-css": "git://github.com/t0m/select2-bootstrap-css.git#v1.3.1",
|
|
81
|
-
"source-sans
|
|
81
|
+
"source-sans": "3.46.0",
|
|
82
82
|
"spel2js": "0.2.6",
|
|
83
83
|
"ui-select": "0.19.8"
|
|
84
84
|
},
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"@graphql-codegen/typescript-operations": "^1.18.3",
|
|
90
90
|
"@graphql-codegen/typescript-react-apollo": "^2.3.0",
|
|
91
91
|
"@spinnaker/eslint-plugin": "^1.1.1",
|
|
92
|
-
"@spinnaker/scripts": "^0.
|
|
92
|
+
"@spinnaker/scripts": "^0.2.0",
|
|
93
93
|
"@types/angular": "1.6.26",
|
|
94
94
|
"@types/angular-mocks": "1.5.10",
|
|
95
95
|
"@types/angular-ui-bootstrap": "0.13.41",
|
|
@@ -120,5 +120,5 @@
|
|
|
120
120
|
"shx": "0.3.3",
|
|
121
121
|
"typescript": "4.3.5"
|
|
122
122
|
},
|
|
123
|
-
"gitHead": "
|
|
123
|
+
"gitHead": "dc5b117bb381113416c21ae8e4c39ec1c526dd2b"
|
|
124
124
|
}
|
|
@@ -12,11 +12,6 @@ export interface ICloudProviderLogoProps {
|
|
|
12
12
|
showTooltip?: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export interface ICloudProviderLogoState {
|
|
16
|
-
tooltip?: string;
|
|
17
|
-
logo: React.ComponentType<React.SVGProps<HTMLOrSVGElement>>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
15
|
export const CloudProviderLogo = ({ height, provider, showTooltip, width }: ICloudProviderLogoProps) => {
|
|
21
16
|
const [tooltip, setTooltip] = React.useState<string>(undefined);
|
|
22
17
|
const RegistryLogo = CloudProviderRegistry.getValue(provider, 'cloudProviderLogo');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* tslint:disable: no-console */
|
|
2
|
-
import { cloneDeep,
|
|
2
|
+
import { cloneDeep, get, isNil, set } from 'lodash';
|
|
3
3
|
|
|
4
4
|
import { SETTINGS } from '../config/settings';
|
|
5
5
|
|
|
@@ -13,37 +13,11 @@ export interface ICloudProviderConfig {
|
|
|
13
13
|
[attribute: string]: any;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
class Providers {
|
|
17
|
-
private providers: Array<{ cloudProvider: string; config: ICloudProviderConfig }> = [];
|
|
18
|
-
|
|
19
|
-
public set(cloudProvider: string, config: ICloudProviderConfig): void {
|
|
20
|
-
// The original implementation used a Map, so calling #set could overwrite a config.
|
|
21
|
-
// The tests depend on this behavior, but maybe something else does as well.
|
|
22
|
-
this.providers = without(
|
|
23
|
-
this.providers,
|
|
24
|
-
this.providers.find((p) => p.cloudProvider === cloudProvider),
|
|
25
|
-
).concat([{ cloudProvider, config }]);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public get(cloudProvider: string): ICloudProviderConfig {
|
|
29
|
-
const provider = this.providers.find((p) => p.cloudProvider === cloudProvider);
|
|
30
|
-
return provider ? provider.config : null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public has(cloudProvider: string): boolean {
|
|
34
|
-
return !!this.get(cloudProvider);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public keys(): string[] {
|
|
38
|
-
return uniq(this.providers.map((p) => p.cloudProvider));
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
16
|
export class CloudProviderRegistry {
|
|
43
17
|
/*
|
|
44
18
|
Note: Providers don't get $log, so we stick with console statements here
|
|
45
19
|
*/
|
|
46
|
-
private static providers = new
|
|
20
|
+
private static providers = new Map<string, ICloudProviderConfig>();
|
|
47
21
|
|
|
48
22
|
public static registerProvider(cloudProvider: string, config: ICloudProviderConfig): void {
|
|
49
23
|
if (SETTINGS.providers[cloudProvider]) {
|
|
@@ -64,19 +38,7 @@ export class CloudProviderRegistry {
|
|
|
64
38
|
console.warn(`Cannot override "${key}" for provider "${cloudProvider}" (provider not registered)`);
|
|
65
39
|
return;
|
|
66
40
|
}
|
|
67
|
-
|
|
68
|
-
const parentKeys = key.split('.');
|
|
69
|
-
const lastKey = parentKeys.pop();
|
|
70
|
-
let current = config;
|
|
71
|
-
|
|
72
|
-
parentKeys.forEach((parentKey) => {
|
|
73
|
-
if (!current[parentKey]) {
|
|
74
|
-
current[parentKey] = {};
|
|
75
|
-
}
|
|
76
|
-
current = current[parentKey];
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
current[lastKey] = overrideValue;
|
|
41
|
+
set(this.providers.get(cloudProvider), key, overrideValue);
|
|
80
42
|
}
|
|
81
43
|
|
|
82
44
|
public static hasValue(cloudProvider: string, key: string) {
|
|
@@ -84,26 +46,7 @@ export class CloudProviderRegistry {
|
|
|
84
46
|
}
|
|
85
47
|
|
|
86
48
|
public static getValue(cloudProvider: string, key: string): any {
|
|
87
|
-
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
const config = this.getProvider(cloudProvider);
|
|
91
|
-
const keyParts = key.split('.');
|
|
92
|
-
let current = config;
|
|
93
|
-
let notFound = false;
|
|
94
|
-
|
|
95
|
-
keyParts.forEach((keyPart) => {
|
|
96
|
-
if (!notFound && current.hasOwnProperty(keyPart)) {
|
|
97
|
-
current = current[keyPart];
|
|
98
|
-
} else {
|
|
99
|
-
notFound = true;
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
if (notFound) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
return current;
|
|
49
|
+
return get(this.getProvider(cloudProvider), key) ?? null;
|
|
107
50
|
}
|
|
108
51
|
|
|
109
52
|
//If the flag kubernetesAdHocInfraWritesEnabled is set to "false" then is disabled
|
|
@@ -111,9 +54,7 @@ export class CloudProviderRegistry {
|
|
|
111
54
|
if (cloudProvider !== 'kubernetes') {
|
|
112
55
|
return false;
|
|
113
56
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
CloudProviderRegistry.getValue(cloudProvider, 'kubernetesAdHocInfraWritesEnabled') === false
|
|
117
|
-
);
|
|
57
|
+
const writesEnabled = CloudProviderRegistry.getValue(cloudProvider, 'kubernetesAdHocInfraWritesEnabled');
|
|
58
|
+
return isNil(writesEnabled) || writesEnabled === false;
|
|
118
59
|
}
|
|
119
60
|
}
|
|
@@ -266,4 +266,100 @@ describe('ProviderSelectionService: API', () => {
|
|
|
266
266
|
$scope.$digest();
|
|
267
267
|
expect(provider).toBe('modalProvider');
|
|
268
268
|
});
|
|
269
|
+
|
|
270
|
+
// Unit tests for the isDisabled function, used to disable and enable buttons that create infrastructure ad-hoc operations
|
|
271
|
+
// in the core module (Create Server Group, Create Load Balancer, Create Firewall, Create Function)
|
|
272
|
+
describe('Toggle Infrastructure Ad-hoc Operations', function () {
|
|
273
|
+
// If an application is configured to only have kubernetes as a cloud provider and only one account exists, which is a kubernetes account,
|
|
274
|
+
// then show the create infrastructure buttons if kubernetesAdHocInfraWritesEnabled is set to true
|
|
275
|
+
it('create infrastructure buttons are enabled for applications with kubernetes cloud provider when kubernetesAdHocInfraWritesEnabled is set to true', () => {
|
|
276
|
+
let isDisabled_result = false;
|
|
277
|
+
hasValue = true;
|
|
278
|
+
const k8s_account = fakeAccount('kubernetes');
|
|
279
|
+
k8s_account.type = 'kubernetes';
|
|
280
|
+
|
|
281
|
+
accounts = [k8s_account];
|
|
282
|
+
let configuration = {
|
|
283
|
+
name: 'kubernetes',
|
|
284
|
+
kubernetesAdHocInfraWritesEnabled: true,
|
|
285
|
+
};
|
|
286
|
+
CloudProviderRegistry.registerProvider('kubernetes', configuration);
|
|
287
|
+
ProviderSelectionService.isDisabled(application).then((isDisable) => {
|
|
288
|
+
isDisabled_result = isDisable;
|
|
289
|
+
});
|
|
290
|
+
$scope.$digest();
|
|
291
|
+
expect(isDisabled_result).toBe(false);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// If an application is configured to only have kubernetes as a cloud provider and only one account exists, which is a kubernetes account,
|
|
295
|
+
// then disable the create infrastructure buttons when kubernetesAdHocInfraWritesEnabled is set to false
|
|
296
|
+
it('disable create infrastructure buttons for kubernetes applications when kubernetesAdHocInfraWritesEnabled is false', () => {
|
|
297
|
+
let isDisabled_result = false;
|
|
298
|
+
hasValue = true;
|
|
299
|
+
const k8s_account = fakeAccount('kubernetes');
|
|
300
|
+
k8s_account.type = 'kubernetes';
|
|
301
|
+
|
|
302
|
+
accounts = [k8s_account];
|
|
303
|
+
let configuration = {
|
|
304
|
+
name: 'kubernetes',
|
|
305
|
+
kubernetesAdHocInfraWritesEnabled: false,
|
|
306
|
+
};
|
|
307
|
+
CloudProviderRegistry.registerProvider('kubernetes', configuration);
|
|
308
|
+
ProviderSelectionService.isDisabled(application).then((isDisable) => {
|
|
309
|
+
isDisabled_result = isDisable;
|
|
310
|
+
});
|
|
311
|
+
$scope.$digest();
|
|
312
|
+
expect(isDisabled_result).toBe(true);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// If the application is configured to have multiple cloud providers (kuberentes and gce) and different accounts exist with
|
|
316
|
+
// different cloud providers (kuberentes and gce), then create infrastructure buttons appear even though kubernetesAdHocInfraWritesEnabled is false.
|
|
317
|
+
// This is because the buttons allow for ad-hoc operations for the non-kubernetes provider (GCE in this case)
|
|
318
|
+
it('create infrastructure buttons are enabled for apps with a cloud provider that does not have its ad-hoc operation disabled', () => {
|
|
319
|
+
let provider = '';
|
|
320
|
+
hasValue = true;
|
|
321
|
+
let isDisabled_result = false;
|
|
322
|
+
const k8s_account = fakeAccount('kubernetes');
|
|
323
|
+
k8s_account.type = 'kubernetes';
|
|
324
|
+
accounts = [k8s_account, fakeAccount('gce')];
|
|
325
|
+
let kubernetes_configuration = {
|
|
326
|
+
name: 'Kubernetes',
|
|
327
|
+
kubernetesAdHocInfraWritesEnabled: false,
|
|
328
|
+
};
|
|
329
|
+
CloudProviderRegistry.registerProvider('kubernetes', kubernetes_configuration);
|
|
330
|
+
CloudProviderRegistry.registerProvider('gce', config);
|
|
331
|
+
ProviderSelectionService.selectProvider(application, 'securityGroup').then((_provider) => {
|
|
332
|
+
provider = _provider;
|
|
333
|
+
});
|
|
334
|
+
ProviderSelectionService.isDisabled(application).then((isDisable) => {
|
|
335
|
+
isDisabled_result = isDisable;
|
|
336
|
+
});
|
|
337
|
+
$scope.$digest();
|
|
338
|
+
expect(isDisabled_result).toBe(false);
|
|
339
|
+
expect(provider).toBe('gce');
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// If an application is configured to have kubernetes as a cloud provider, and there are multiple kubernetes accounts, then the create
|
|
343
|
+
// infrastructure buttons are disabled if kubernetesAdHocInfraWritesEnabled is false
|
|
344
|
+
it('create infrastructure buttons are disabled when all accounts have cloud providers with ad-hoc operations disabled', () => {
|
|
345
|
+
let isDisabled_result = false;
|
|
346
|
+
hasValue = true;
|
|
347
|
+
const k8s_account_1 = fakeAccount('kubernetes');
|
|
348
|
+
k8s_account_1.type = 'kubernetes';
|
|
349
|
+
const k8s_account_2 = fakeAccount('kubernetes');
|
|
350
|
+
k8s_account_2.type = 'kubernetes';
|
|
351
|
+
|
|
352
|
+
accounts = [k8s_account_1, k8s_account_2];
|
|
353
|
+
let configuration = {
|
|
354
|
+
name: 'kubernetes',
|
|
355
|
+
kubernetesAdHocInfraWritesEnabled: false,
|
|
356
|
+
};
|
|
357
|
+
CloudProviderRegistry.registerProvider('kubernetes', configuration);
|
|
358
|
+
ProviderSelectionService.isDisabled(application).then((isDisable) => {
|
|
359
|
+
isDisabled_result = isDisable;
|
|
360
|
+
});
|
|
361
|
+
$scope.$digest();
|
|
362
|
+
expect(isDisabled_result).toBe(true);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
269
365
|
});
|
|
@@ -53,14 +53,12 @@ export class ProviderSelectionService {
|
|
|
53
53
|
public static isDisabled(app: Application): PromiseLike<boolean> {
|
|
54
54
|
return AccountService.applicationAccounts(app).then((accounts: IAccountDetails[]) => {
|
|
55
55
|
let isDisable = false;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
isDisable = !CloudProviderRegistry.getValue(a.cloudProvider, 'kubernetesAdHocInfraWritesEnabled');
|
|
63
|
-
});
|
|
56
|
+
const cloudProvidersEnabled = accounts.filter((a) => {
|
|
57
|
+
return !CloudProviderRegistry.isDisabled(a.cloudProvider);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (cloudProvidersEnabled.length === 0) {
|
|
61
|
+
isDisable = true;
|
|
64
62
|
}
|
|
65
63
|
return isDisable;
|
|
66
64
|
});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
class="btn btn-xs btn-default"
|
|
7
7
|
ng-class="{active: filterModel.sortFilter.multiselect}"
|
|
8
8
|
ng-click="ctrl.toggleMultiselect()"
|
|
9
|
+
ng-hide="isDisabled"
|
|
9
10
|
>
|
|
10
11
|
<span class="glyphicon glyphicon-list visible-lg-inline"></span>
|
|
11
12
|
<span class="glyphicon glyphicon-list visible-md-inline visible-sm-inline" uib-tooltip="Edit multiple"></span>
|
package/src/config/settings.ts
CHANGED
|
@@ -73,6 +73,7 @@ export interface IManagedDeliveryURLs {
|
|
|
73
73
|
pinning: string;
|
|
74
74
|
resourceStatus: string;
|
|
75
75
|
markAsBad: string;
|
|
76
|
+
previewEnvironments?: string;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
export interface ISpinnakerSettings {
|
|
@@ -157,6 +158,7 @@ export const SETTINGS: ISpinnakerSettings = (window as any).spinnakerSettings ||
|
|
|
157
158
|
// Make sure to set up some reasonable default settings fields so we do not have to keep checking if they exist everywhere
|
|
158
159
|
SETTINGS.feature = SETTINGS.feature || {};
|
|
159
160
|
SETTINGS.feature.roscoMode = SETTINGS.feature.roscoMode ?? true;
|
|
161
|
+
SETTINGS.kubernetesAdHocInfraWritesEnabled = SETTINGS.kubernetesAdHocInfraWritesEnabled ?? true;
|
|
160
162
|
SETTINGS.analytics = SETTINGS.analytics || {};
|
|
161
163
|
SETTINGS.providers = SETTINGS.providers || {};
|
|
162
164
|
SETTINGS.defaultTimeZone = SETTINGS.defaultTimeZone || 'America/Los_Angeles';
|
package/src/core.module.ts
CHANGED
|
@@ -14,7 +14,7 @@ import 'ui-select/dist/select.css';
|
|
|
14
14
|
import '@spinnaker/styleguide/public/styleguide.min.css';
|
|
15
15
|
import 'Select2/select2.css';
|
|
16
16
|
import 'select2-bootstrap-css/select2-bootstrap.css';
|
|
17
|
-
import 'source-sans
|
|
17
|
+
import 'source-sans/source-sans-3.css';
|
|
18
18
|
import './fonts/icons.css';
|
|
19
19
|
|
|
20
20
|
import UI_ROUTER from '@uirouter/angularjs';
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import UIROUTER_ANGULARJS from '@uirouter/angularjs';
|
|
4
4
|
import { module } from 'angular';
|
|
5
5
|
|
|
6
|
+
import { ProviderSelectionService } from '../../cloudProvider/providerSelection/ProviderSelectionService';
|
|
6
7
|
import { ConfirmationModalService } from '../../confirmationModal';
|
|
7
8
|
import { InstanceWriter } from '../instance.write.service';
|
|
8
9
|
import { CORE_INSTANCE_DETAILS_MULTIPLEINSTANCESERVERGROUP_DIRECTIVE } from './multipleInstanceServerGroup.directive';
|
|
@@ -20,6 +21,11 @@ module(CORE_INSTANCE_DETAILS_MULTIPLEINSTANCES_CONTROLLER, [
|
|
|
20
21
|
'app',
|
|
21
22
|
function ($scope, $state, app) {
|
|
22
23
|
this.selectedGroups = [];
|
|
24
|
+
this.$onInit = () => {
|
|
25
|
+
ProviderSelectionService.isDisabled(app).then((disabled) => {
|
|
26
|
+
$scope.isDisabled = disabled;
|
|
27
|
+
});
|
|
28
|
+
};
|
|
23
29
|
|
|
24
30
|
/**
|
|
25
31
|
* Actions
|
|
@@ -19,6 +19,7 @@ import { ActionModal, IArtifactActionModalProps } from '../utils/ActionModal';
|
|
|
19
19
|
import { getIsDebugMode } from '../utils/debugMode';
|
|
20
20
|
import { getDocsUrl, MODAL_MAX_WIDTH, spinnerProps } from '../utils/defaults';
|
|
21
21
|
import { useLogEvent } from '../utils/logging';
|
|
22
|
+
import { useNotifyOnError } from '../utils/useNotifyOnError.hook';
|
|
22
23
|
import { Spinner } from '../../widgets';
|
|
23
24
|
|
|
24
25
|
const BTN_CLASSNAMES = 'btn md-btn';
|
|
@@ -82,10 +83,16 @@ interface IManagementToggleProps {
|
|
|
82
83
|
const ManagementToggle = ({ isPaused }: IManagementToggleProps) => {
|
|
83
84
|
const appName = useApplicationContextSafe().name;
|
|
84
85
|
const logEvent = useLogEvent('Management');
|
|
85
|
-
const [toggleManagement, { loading: mutationInFlight }] = useToggleManagementMutation({
|
|
86
|
+
const [toggleManagement, { loading: mutationInFlight, error }] = useToggleManagementMutation({
|
|
86
87
|
refetchQueries: [{ query: FetchApplicationManagementDataDocument, variables: { appName } }],
|
|
87
88
|
});
|
|
88
89
|
|
|
90
|
+
useNotifyOnError({
|
|
91
|
+
key: 'toggleManagement',
|
|
92
|
+
content: `Failed to ${isPaused ? 'enable' : 'disable'} management`,
|
|
93
|
+
error,
|
|
94
|
+
});
|
|
95
|
+
|
|
89
96
|
const onShowToggleManagementModal = React.useCallback((shouldPause: boolean) => {
|
|
90
97
|
logEvent({ action: 'OpenModal', data: { shouldPause } });
|
|
91
98
|
showModal(
|
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
import { useApplicationContextSafe } from '../../presentation/hooks/useApplicationContext.hook';
|
|
10
10
|
import { YamlViewer } from '../utils/YamlViewer';
|
|
11
11
|
import { useLogEvent } from '../utils/logging';
|
|
12
|
-
import {
|
|
12
|
+
import { useNotifyOnError } from '../utils/useNotifyOnError.hook';
|
|
13
|
+
import { Spinner } from '../../widgets';
|
|
13
14
|
|
|
14
15
|
interface IDeliveryConfigProps {
|
|
15
16
|
config?: string;
|
|
@@ -26,14 +27,7 @@ const ReImportConfig = () => {
|
|
|
26
27
|
});
|
|
27
28
|
const logEvent = useLogEvent('GitIntegration', 'ImportNow');
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
if (!error) return;
|
|
31
|
-
NotifierService.publish({
|
|
32
|
-
key: 'import-error',
|
|
33
|
-
content: `Failed to import delivery config - ${error.message}`,
|
|
34
|
-
options: { type: 'error' },
|
|
35
|
-
});
|
|
36
|
-
}, [error]);
|
|
30
|
+
useNotifyOnError({ key: 'import-error', content: `Failed to import delivery config`, error });
|
|
37
31
|
|
|
38
32
|
return (
|
|
39
33
|
<>
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from '../graphql/graphql-sdk';
|
|
9
9
|
import { CheckboxInput, useApplicationContextSafe } from '../../presentation';
|
|
10
10
|
import { useLogEvent } from '../utils/logging';
|
|
11
|
+
import { useNotifyOnError } from '../utils/useNotifyOnError.hook';
|
|
11
12
|
import { Spinner } from '../../widgets/spinners/Spinner';
|
|
12
13
|
|
|
13
14
|
import './GitIntegration.less';
|
|
@@ -79,11 +80,17 @@ const ManifestPath = ({ manifestPath }: Pick<IGitIntegrationProps, 'manifestPath
|
|
|
79
80
|
|
|
80
81
|
export const GitIntegration = ({ isEnabled, branch, link, repository, manifestPath }: IGitIntegrationProps) => {
|
|
81
82
|
const appName = useApplicationContextSafe().name;
|
|
82
|
-
const [updateIntegration, { loading }] = useUpdateGitIntegrationMutation({
|
|
83
|
+
const [updateIntegration, { loading, error }] = useUpdateGitIntegrationMutation({
|
|
83
84
|
refetchQueries: [{ query: FetchApplicationManagementDataDocument, variables: { appName } }],
|
|
84
85
|
});
|
|
85
86
|
const logEvent = useLogEvent('GitIntegration');
|
|
86
87
|
|
|
88
|
+
useNotifyOnError({
|
|
89
|
+
key: 'toggleGitIntegration',
|
|
90
|
+
content: `Failed to ${isEnabled ? 'disable' : 'enable'} auto-import`,
|
|
91
|
+
error,
|
|
92
|
+
});
|
|
93
|
+
|
|
87
94
|
const repoAndBranch = [repository, branch].join(':');
|
|
88
95
|
|
|
89
96
|
return (
|
|
@@ -65,10 +65,16 @@ interface IEnvironmentsRenderProps {
|
|
|
65
65
|
children?: React.ReactNode;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
export interface OrderedEnvironments<T> {
|
|
69
|
+
className?: string;
|
|
70
|
+
style?: React.CSSProperties;
|
|
71
|
+
environments: T[];
|
|
72
|
+
}
|
|
73
|
+
|
|
68
74
|
export const useOrderedEnvironment = <T extends object>(
|
|
69
75
|
ref: React.RefObject<HTMLDivElement>,
|
|
70
76
|
environments: T[],
|
|
71
|
-
):
|
|
77
|
+
): OrderedEnvironments<T> => {
|
|
72
78
|
const { direction } = useEnvironmentDirectionState();
|
|
73
79
|
const { width } = useDimensions(ref, { isActive: direction === 'gridView' });
|
|
74
80
|
|