1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 09:13:33 +00:00

Revert "[EC-73] edit collection modal (#3638)"

This reverts commit 39655ebe29.
This commit is contained in:
Shane Melton
2022-11-22 10:08:19 -08:00
parent 39655ebe29
commit cbb22230fc
57 changed files with 420 additions and 1225 deletions

View File

@@ -28,7 +28,6 @@ import {
InternalFolderService,
} from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction } from "@bitwarden/common/abstractions/formValidationErrors.service";
import { GroupServiceAbstraction } from "@bitwarden/common/abstractions/group";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service";
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/abstractions/keyConnector.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
@@ -89,7 +88,6 @@ import { FileUploadService } from "@bitwarden/common/services/fileUpload.service
import { FolderApiService } from "@bitwarden/common/services/folder/folder-api.service";
import { FolderService } from "@bitwarden/common/services/folder/folder.service";
import { FormValidationErrorsService } from "@bitwarden/common/services/formValidationErrors.service";
import { GroupService } from "@bitwarden/common/services/group/group.service";
import { KeyConnectorService } from "@bitwarden/common/services/keyConnector.service";
import { LoginService } from "@bitwarden/common/services/login.service";
import { NotificationsService } from "@bitwarden/common/services/notifications.service";
@@ -582,11 +580,6 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
useClass: ValidationService,
deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction],
},
{
provide: GroupServiceAbstraction,
useClass: GroupService,
deps: [ApiServiceAbstraction],
},
{
provide: LoginServiceAbstraction,
useClass: LoginService,

View File

@@ -92,7 +92,7 @@ import { BillingPaymentResponse } from "../models/response/billing-payment.respo
import { BreachAccountResponse } from "../models/response/breach-account.response";
import { CipherResponse } from "../models/response/cipher.response";
import {
CollectionAccessDetailsResponse,
CollectionGroupDetailsResponse,
CollectionResponse,
} from "../models/response/collection.response";
import { DeviceVerificationResponse } from "../models/response/device-verification.response";
@@ -314,7 +314,7 @@ export abstract class ApiService {
getCollectionDetails: (
organizationId: string,
id: string
) => Promise<CollectionAccessDetailsResponse>;
) => Promise<CollectionGroupDetailsResponse>;
getUserCollections: () => Promise<ListResponse<CollectionResponse>>;
getCollections: (organizationId: string) => Promise<ListResponse<CollectionResponse>>;
getCollectionUsers: (organizationId: string, id: string) => Promise<SelectionReadOnlyResponse[]>;

View File

@@ -1,9 +0,0 @@
import { GroupView } from "../../models/view/group-view";
export class GroupServiceAbstraction {
delete: (orgId: string, groupId: string) => Promise<void>;
deleteMany: (orgId: string, groupIds: string[]) => Promise<GroupView[]>;
get: (orgId: string, groupId: string) => Promise<GroupView>;
getAll: (orgId: string) => Promise<GroupView[]>;
}

View File

@@ -1,2 +0,0 @@
export * from "./group.service.abstraction";
export * from "./responses/group-response";

View File

@@ -1,31 +0,0 @@
import { BaseResponse } from "../../../models/response/base.response";
import { SelectionReadOnlyResponse } from "../../../models/response/selection-read-only.response";
export class GroupResponse extends BaseResponse {
id: string;
organizationId: string;
name: string;
accessAll: boolean;
externalId: string;
constructor(response: any) {
super(response);
this.id = this.getResponseProperty("Id");
this.organizationId = this.getResponseProperty("OrganizationId");
this.name = this.getResponseProperty("Name");
this.accessAll = this.getResponseProperty("AccessAll");
this.externalId = this.getResponseProperty("ExternalId");
}
}
export class GroupDetailsResponse extends GroupResponse {
collections: SelectionReadOnlyResponse[] = [];
constructor(response: any) {
super(response);
const collections = this.getResponseProperty("Collections");
if (collections != null) {
this.collections = collections.map((c: any) => new SelectionReadOnlyResponse(c));
}
}
}

View File

@@ -440,10 +440,6 @@ export class Utils {
return mobile || win.navigator.userAgent.match(/iPad/i) != null;
}
static delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
private static isAppleMobile(win: Window) {
return (
win.navigator.userAgent.match(/iPhone/i) != null ||

View File

@@ -6,7 +6,6 @@ export class CollectionRequest {
name: string;
externalId: string;
groups: SelectionReadOnlyRequest[] = [];
users: SelectionReadOnlyRequest[] = [];
constructor(collection?: Collection) {
if (collection == null) {

View File

@@ -25,9 +25,8 @@ export class CollectionDetailsResponse extends CollectionResponse {
}
}
export class CollectionAccessDetailsResponse extends CollectionResponse {
export class CollectionGroupDetailsResponse extends CollectionResponse {
groups: SelectionReadOnlyResponse[] = [];
users: SelectionReadOnlyResponse[] = [];
constructor(response: any) {
super(response);
@@ -35,10 +34,5 @@ export class CollectionAccessDetailsResponse extends CollectionResponse {
if (groups != null) {
this.groups = groups.map((g: any) => new SelectionReadOnlyResponse(g));
}
const users = this.getResponseProperty("Users");
if (users != null) {
this.users = users.map((g: any) => new SelectionReadOnlyResponse(g));
}
}
}

View File

@@ -1,6 +1,6 @@
import { Collection } from "../domain/collection";
import { ITreeNodeObject } from "../domain/tree-node";
import { CollectionResponse } from "../response/collection.response";
import { CollectionGroupDetailsResponse } from "../response/collection.response";
import { View } from "./view";
@@ -12,7 +12,7 @@ export class CollectionView implements View, ITreeNodeObject {
readOnly: boolean = null;
hidePasswords: boolean = null;
constructor(c?: Collection | CollectionResponse) {
constructor(c?: Collection | CollectionGroupDetailsResponse) {
if (!c) {
return;
}

View File

@@ -1,17 +0,0 @@
import { GroupResponse } from "../../abstractions/group";
import { SelectionReadOnlyResponse } from "../response/selection-read-only.response";
import { View } from "./view";
export class GroupView implements View {
id: string;
organizationId: string;
name: string;
accessAll: boolean;
externalId: string;
collections: SelectionReadOnlyResponse[] = [];
static fromResponse(response: GroupResponse) {
return Object.assign(new GroupView(), response);
}
}

View File

@@ -100,7 +100,7 @@ import { BillingPaymentResponse } from "../models/response/billing-payment.respo
import { BreachAccountResponse } from "../models/response/breach-account.response";
import { CipherResponse } from "../models/response/cipher.response";
import {
CollectionAccessDetailsResponse,
CollectionGroupDetailsResponse,
CollectionResponse,
} from "../models/response/collection.response";
import { DeviceVerificationResponse } from "../models/response/device-verification.response";
@@ -810,7 +810,7 @@ export class ApiService implements ApiServiceAbstraction {
async getCollectionDetails(
organizationId: string,
id: string
): Promise<CollectionAccessDetailsResponse> {
): Promise<CollectionGroupDetailsResponse> {
const r = await this.send(
"GET",
"/organizations/" + organizationId + "/collections/" + id + "/details",
@@ -818,7 +818,7 @@ export class ApiService implements ApiServiceAbstraction {
true,
true
);
return new CollectionAccessDetailsResponse(r);
return new CollectionGroupDetailsResponse(r);
}
async getUserCollections(): Promise<ListResponse<CollectionResponse>> {

View File

@@ -1,65 +0,0 @@
import { ApiService } from "../../abstractions/api.service";
import {
GroupDetailsResponse,
GroupResponse,
GroupServiceAbstraction,
} from "../../abstractions/group";
import { ListResponse } from "../../models/response/list.response";
import { GroupView } from "../../models/view/group-view";
import { OrganizationGroupBulkRequest } from "./requests/organization-group-bulk-request";
export class GroupService implements GroupServiceAbstraction {
constructor(private apiService: ApiService) {}
async delete(orgId: string, groupId: string): Promise<void> {
await this.apiService.send(
"DELETE",
"/organizations/" + orgId + "/groups/" + groupId,
null,
true,
false
);
}
async deleteMany(orgId: string, groupIds: string[]): Promise<GroupView[]> {
const request = new OrganizationGroupBulkRequest(groupIds);
const r = await this.apiService.send(
"DELETE",
"/organizations/" + orgId + "/groups",
request,
true,
true
);
const listResponse = new ListResponse(r, GroupResponse);
return listResponse.data?.map((gr) => GroupView.fromResponse(gr)) ?? [];
}
async get(orgId: string, groupId: string): Promise<GroupView> {
const r = await this.apiService.send(
"GET",
"/organizations/" + orgId + "/groups/" + groupId + "/details",
null,
true,
true
);
return GroupView.fromResponse(new GroupDetailsResponse(r));
}
async getAll(orgId: string): Promise<GroupView[]> {
const r = await this.apiService.send(
"GET",
"/organizations/" + orgId + "/groups",
null,
true,
true
);
const listResponse = new ListResponse(r, GroupDetailsResponse);
return listResponse.data?.map((gr) => GroupView.fromResponse(gr)) ?? [];
}
}

View File

@@ -1,7 +0,0 @@
export class OrganizationGroupBulkRequest {
ids: string[];
constructor(ids: string[]) {
this.ids = ids == null ? [] : ids;
}
}

View File

@@ -3,9 +3,10 @@ import { Subject, takeUntil } from "rxjs";
import { ButtonLikeAbstraction } from "../shared/button-like.abstraction";
import { BitActionDirective } from "./bit-action.directive";
import { BitSubmitDirective } from "./bit-submit.directive";
import { BitActionDirective } from ".";
/**
* This directive has two purposes:
*

View File

@@ -1,6 +1,6 @@
import { Component, Input, OnChanges } from "@angular/core";
import { BadgeType } from "../badge";
import { BadgeTypes } from "../badge";
@Component({
selector: "bit-badge-list",
@@ -12,7 +12,7 @@ export class BadgeListComponent implements OnChanges {
protected filteredItems: string[] = [];
protected isFiltered = false;
@Input() badgeType: BadgeType = "primary";
@Input() badgeType: BadgeTypes = "primary";
@Input() items: string[] = [];
@Input()

View File

@@ -1,8 +1,8 @@
import { Directive, ElementRef, HostBinding, Input } from "@angular/core";
export type BadgeType = "primary" | "secondary" | "success" | "danger" | "warning" | "info";
export type BadgeTypes = "primary" | "secondary" | "success" | "danger" | "warning" | "info";
const styles: Record<BadgeType, string[]> = {
const styles: Record<BadgeTypes, string[]> = {
primary: ["tw-bg-primary-500"],
secondary: ["tw-bg-text-muted"],
success: ["tw-bg-success-500"],
@@ -11,7 +11,7 @@ const styles: Record<BadgeType, string[]> = {
info: ["tw-bg-info-500"],
};
const hoverStyles: Record<BadgeType, string[]> = {
const hoverStyles: Record<BadgeTypes, string[]> = {
primary: ["hover:tw-bg-primary-700"],
secondary: ["hover:tw-bg-secondary-700"],
success: ["hover:tw-bg-success-700"],
@@ -47,7 +47,7 @@ export class BadgeDirective {
.concat(this.hasHoverEffects ? hoverStyles[this.badgeType] : []);
}
@Input() badgeType: BadgeType = "primary";
@Input() badgeType: BadgeTypes = "primary";
private hasHoverEffects = false;

View File

@@ -1,7 +1,7 @@
import { CommonModule } from "@angular/common";
import { Meta, moduleMetadata, Story } from "@storybook/angular";
import { BadgeDirective, BadgeType } from "./badge.directive";
import { BadgeDirective } from "./badge.directive";
export default {
title: "Component Library/Badge",
@@ -15,12 +15,6 @@ export default {
args: {
badgeType: "primary",
},
argTypes: {
badgeType: {
options: ["primary", "secondary", "success", "danger", "warning", "info"] as BadgeType[],
control: { type: "inline-radio" },
},
},
parameters: {
design: {
type: "figma",

View File

@@ -1,2 +1,2 @@
export { BadgeDirective, BadgeType } from "./badge.directive";
export { BadgeDirective, BadgeTypes } from "./badge.directive";
export * from "./badge.module";

View File

@@ -4,7 +4,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { I18nMockService } from "../utils/i18n-mock.service";
import { CalloutComponent } from "./callout.component";
import { CalloutComponent } from ".";
describe("Callout", () => {
let component: CalloutComponent;

View File

@@ -1,56 +0,0 @@
import { FormsModule, ReactiveFormsModule, FormBuilder } from "@angular/forms";
import { Meta, moduleMetadata, Story } from "@storybook/angular";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { ButtonModule } from "../button";
import { InputModule } from "../input/input.module";
import { I18nMockService } from "../utils/i18n-mock.service";
import { forbiddenCharacters } from "./bit-validators/forbidden-characters.validator";
import { BitFormFieldComponent } from "./form-field.component";
import { FormFieldModule } from "./form-field.module";
export default {
title: "Component Library/Form/Custom Validators",
component: BitFormFieldComponent,
decorators: [
moduleMetadata({
imports: [FormsModule, ReactiveFormsModule, FormFieldModule, InputModule, ButtonModule],
providers: [
{
provide: I18nService,
useFactory: () => {
return new I18nMockService({
inputForbiddenCharacters: (chars) =>
`The following characters are not allowed: ${chars}`,
});
},
},
],
}),
],
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/file/f32LSg3jaegICkMu7rPARm/Tailwind-Component-Library-Update?node-id=1881%3A17689",
},
},
} as Meta;
const template = `
<form [formGroup]="formObj">
<bit-form-field>
<bit-label>Name</bit-label>
<input bitInput formControlName="name" />
</bit-form-field>
</form>`;
export const ForbiddenCharacters: Story<BitFormFieldComponent> = (args: BitFormFieldComponent) => ({
props: {
formObj: new FormBuilder().group({
name: ["", forbiddenCharacters(["\\", "/", "@", "#", "$", "%", "^", "&", "*", "(", ")"])],
}),
},
template,
});

View File

@@ -1,45 +0,0 @@
import { FormControl } from "@angular/forms";
import { forbiddenCharacters } from "./forbidden-characters.validator";
describe("forbiddenCharacters", () => {
it("should return no error when input is null", () => {
const input = createControl(null);
const validate = forbiddenCharacters(["n", "u", "l", "l"]);
const errors = validate(input);
expect(errors).toBe(null);
});
it("should return no error when no characters are forbidden", () => {
const input = createControl("special characters: \\/@#$%^&*()");
const validate = forbiddenCharacters([]);
const errors = validate(input);
expect(errors).toBe(null);
});
it("should return no error when input does not contain forbidden characters", () => {
const input = createControl("contains no special characters");
const validate = forbiddenCharacters(["\\", "/", "@", "#", "$", "%", "^", "&", "*", "(", ")"]);
const errors = validate(input);
expect(errors).toBe(null);
});
it("should return error when input contains forbidden characters", () => {
const input = createControl("contains / illegal @ characters");
const validate = forbiddenCharacters(["\\", "/", "@", "#", "$", "%", "^", "&", "*", "(", ")"]);
const errors = validate(input);
expect(errors).not.toBe(null);
});
});
function createControl(input: string) {
return new FormControl(input);
}

View File

@@ -1,23 +0,0 @@
import { AbstractControl, FormControl, ValidationErrors, ValidatorFn } from "@angular/forms";
export function forbiddenCharacters(characters: string[]): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!(control instanceof FormControl)) {
throw new Error("forbiddenCharacters only supports validating FormControls");
}
if (control.value === null || control.value === undefined) {
return null;
}
const value = String(control.value);
for (const char of value) {
if (characters.includes(char)) {
return { forbiddenCharacters: { value: control.value, characters } };
}
}
return null;
};
}

View File

@@ -1 +0,0 @@
export { forbiddenCharacters } from "./forbidden-characters.validator";

View File

@@ -30,8 +30,6 @@ export class BitErrorComponent {
return this.i18nService.t("inputMinLength", this.error[1]?.requiredLength);
case "maxlength":
return this.i18nService.t("inputMaxLength", this.error[1]?.requiredLength);
case "forbiddenCharacters":
return this.i18nService.t("inputForbiddenCharacters", this.error[1]?.characters.join(", "));
default:
// Attempt to show a custom error message.
if (this.error[1]?.message) {

View File

@@ -1,4 +1,3 @@
export * from "./form-field.module";
export * from "./form-field.component";
export * from "./form-field-control";
export * as BitValidators from "./bit-validators";

View File

@@ -1,2 +1 @@
export * from "./multi-select.module";
export * from "./models/select-item-view";