Browse Source

Versioning improved (pure immutability!)

pull/118/head
Sebastian Stehle 9 years ago
parent
commit
5b50f27ac5
  1. 5
      src/Squidex/app/features/api/api-area.component.ts
  2. 5
      src/Squidex/app/features/api/pages/graphql/graphql-page.component.ts
  3. 5
      src/Squidex/app/features/assets/pages/assets-page.component.ts
  4. 5
      src/Squidex/app/features/content/pages/content/content-history.component.ts
  5. 11
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  6. 27
      src/Squidex/app/features/content/pages/contents/contents-page.component.ts
  7. 5
      src/Squidex/app/features/content/pages/schemas/schemas-page.component.ts
  8. 5
      src/Squidex/app/features/content/shared/assets-editor.component.ts
  9. 5
      src/Squidex/app/features/content/shared/content-item.component.ts
  10. 5
      src/Squidex/app/features/content/shared/references-editor.component.ts
  11. 5
      src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts
  12. 58
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts
  13. 9
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts
  14. 5
      src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts
  15. 6
      src/Squidex/app/features/settings/pages/clients/clients-page.component.html
  16. 44
      src/Squidex/app/features/settings/pages/clients/clients-page.component.ts
  17. 4
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html
  18. 47
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts
  19. 14
      src/Squidex/app/features/settings/pages/languages/language.component.ts
  20. 6
      src/Squidex/app/features/settings/pages/languages/languages-page.component.html
  21. 76
      src/Squidex/app/features/settings/pages/languages/languages-page.component.ts
  22. 29
      src/Squidex/app/features/settings/pages/plans/plans-page.component.ts
  23. 5
      src/Squidex/app/features/settings/settings-area.component.ts
  24. 7
      src/Squidex/app/features/webhooks/pages/webhook-events-page.component.ts
  25. 12
      src/Squidex/app/features/webhooks/pages/webhooks-page.component.ts
  26. 40
      src/Squidex/app/framework/angular/http-extensions-impl.ts
  27. 14
      src/Squidex/app/framework/utils/version.spec.ts
  28. 10
      src/Squidex/app/framework/utils/version.ts
  29. 13
      src/Squidex/app/shared/components/app.component-base.ts
  30. 21
      src/Squidex/app/shared/components/asset.component.ts
  31. 5
      src/Squidex/app/shared/components/history.component.ts
  32. 8
      src/Squidex/app/shared/guards/resolve-app-languages.guard.spec.ts
  33. 2
      src/Squidex/app/shared/guards/resolve-app-languages.guard.ts
  34. 51
      src/Squidex/app/shared/services/app-clients.service.spec.ts
  35. 59
      src/Squidex/app/shared/services/app-clients.service.ts
  36. 46
      src/Squidex/app/shared/services/app-contributors.service.spec.ts
  37. 52
      src/Squidex/app/shared/services/app-contributors.service.ts
  38. 51
      src/Squidex/app/shared/services/app-languages.service.spec.ts
  39. 59
      src/Squidex/app/shared/services/app-languages.service.ts
  40. 12
      src/Squidex/app/shared/services/apps.service.ts
  41. 44
      src/Squidex/app/shared/services/assets.service.spec.ts
  42. 83
      src/Squidex/app/shared/services/assets.service.ts
  43. 47
      src/Squidex/app/shared/services/contents.service.spec.ts
  44. 109
      src/Squidex/app/shared/services/contents.service.ts
  45. 6
      src/Squidex/app/shared/services/event-consumers.service.ts
  46. 6
      src/Squidex/app/shared/services/history.service.ts
  47. 6
      src/Squidex/app/shared/services/languages.service.ts
  48. 13
      src/Squidex/app/shared/services/plans.service.spec.ts
  49. 40
      src/Squidex/app/shared/services/plans.service.ts
  50. 62
      src/Squidex/app/shared/services/schemas.service.spec.ts
  51. 147
      src/Squidex/app/shared/services/schemas.service.ts
  52. 24
      src/Squidex/app/shared/services/usages.service.ts
  53. 48
      src/Squidex/app/shared/services/users.service.ts
  54. 14
      src/Squidex/app/shared/services/webhooks.service.spec.ts
  55. 33
      src/Squidex/app/shared/services/webhooks.service.ts

5
src/Squidex/app/features/api/api-area.component.ts

@ -10,6 +10,7 @@ import { Component } from '@angular/core';
import {
AppComponentBase,
AppsStoreService,
AuthService,
DialogService
} from 'shared';
@ -19,8 +20,8 @@ import {
templateUrl: './api-area.component.html'
})
export class ApiAreaComponent extends AppComponentBase {
constructor(apps: AppsStoreService, dialogs: DialogService
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
}

5
src/Squidex/app/features/api/pages/graphql/graphql-page.component.ts

@ -18,6 +18,7 @@ const GraphiQL = require('graphiql');
import {
AppComponentBase,
AppsStoreService,
AuthService,
DialogService,
GraphQlService,
LocalStoreService
@ -33,11 +34,11 @@ export class GraphQLPageComponent extends AppComponentBase implements OnInit {
@ViewChild('graphiQLContainer')
public graphiQLContainer: ElementRef;
constructor(apps: AppsStoreService, dialogs: DialogService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly graphQlService: GraphQlService,
private readonly localStoreService: LocalStoreService
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnInit() {

5
src/Squidex/app/features/assets/pages/assets-page.component.ts

@ -17,6 +17,7 @@ import {
AssetDto,
AssetsService,
AssetUpdated,
AuthService,
DialogService,
ImmutableArray,
MessageBus,
@ -38,11 +39,11 @@ export class AssetsPageComponent extends AppComponentBase implements OnDestroy,
public assetsFilter = new FormControl();
public assertQuery = '';
constructor(apps: AppsStoreService, dialogs: DialogService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly assetsService: AssetsService,
private readonly messageBus: MessageBus
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnInit() {

5
src/Squidex/app/features/content/pages/content/content-history.component.ts

@ -13,6 +13,7 @@ import {
allParams,
AppComponentBase,
AppsStoreService,
AuthService,
DialogService,
HistoryChannelUpdated,
HistoryEventDto,
@ -55,13 +56,13 @@ export class ContentHistoryComponent extends AppComponentBase {
.switchMap(() => this.appNameOnce())
.switchMap(app => this.historyService.getHistory(app, this.channel).retry(2));
constructor(appsStore: AppsStoreService, dialogs: DialogService,
constructor(appsStore: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly users: UsersProviderService,
private readonly historyService: HistoryService,
private readonly messageBus: MessageBus,
private readonly route: ActivatedRoute
) {
super(dialogs, appsStore);
super(dialogs, appsStore, authService);
}
private userName(userId: string): Observable<string> {

11
src/Squidex/app/features/content/pages/content/content-page.component.ts

@ -55,14 +55,13 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
public languages: AppLanguageDto[] = [];
constructor(apps: AppsStoreService, dialogs: DialogService,
private readonly authService: AuthService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly contentsService: ContentsService,
private readonly route: ActivatedRoute,
private readonly router: Router,
private readonly messageBus: MessageBus
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnDestroy() {
@ -87,7 +86,7 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
this.messageBus.of(ContentPublished)
.subscribe(message => {
if (this.content && message.content.id === this.content.id) {
this.content = this.content.replaceVersion(message.content.version);
this.content = this.content.publish(message.content.lastModifiedBy, message.content.version, message.content.lastModified);
}
});
@ -95,7 +94,7 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
this.messageBus.of(ContentUnpublished)
.subscribe(message => {
if (this.content && message.content.id === this.content.id) {
this.content = this.content.replaceVersion(message.content.version);
this.content = this.content.unpublish(message.content.lastModifiedBy, message.content.version, message.content.lastModified);
}
});
@ -158,7 +157,7 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
this.appNameOnce()
.switchMap(app => this.contentsService.putContent(app, this.schema.name, this.content.id, requestDto, this.content.version))
.subscribe(dto => {
this.content = this.content.update(dto, this.authService.user!.token);
this.content = this.content.update(dto.payload, this.userToken, dto.version);
this.emitContentUpdated(this.content);
this.notifyInfo('Content saved successfully.');

27
src/Squidex/app/features/content/pages/contents/contents-page.component.ts

@ -63,13 +63,12 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
public columnWidth: number;
constructor(apps: AppsStoreService, dialogs: DialogService,
private readonly authService: AuthService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly contentsService: ContentsService,
private readonly route: ActivatedRoute,
private readonly messageBus: MessageBus
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnDestroy() {
@ -92,7 +91,7 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
this.contentUpdatedSubscription =
this.messageBus.of(ContentUpdated)
.subscribe(message => {
this.contentItems = this.contentItems.replaceBy('id', message.content, (o, n) => o.update(n.data, n.lastModifiedBy));
this.contentItems = this.contentItems.replaceBy('id', message.content, (o, n) => o.update(n.data, n.lastModifiedBy, n.version, n.lastModified));
});
this.route.params.map(p => <string> p['language'])
@ -118,8 +117,10 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
public publishContent(content: ContentDto) {
this.appNameOnce()
.switchMap(app => this.contentsService.publishContent(app, this.schema.name, content.id, content.version))
.subscribe(() => {
this.contentItems = this.contentItems.replaceBy('id', content.publish(this.authService.user!.token));
.subscribe(dto => {
content = content.publish(this.userToken, dto.version);
this.contentItems = this.contentItems.replaceBy('id', content);
this.emitContentPublished(content);
}, error => {
@ -130,8 +131,10 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
public unpublishContent(content: ContentDto) {
this.appNameOnce()
.switchMap(app => this.contentsService.unpublishContent(app, this.schema.name, content.id, content.version))
.subscribe(() => {
this.contentItems = this.contentItems.replaceBy('id', content.unpublish(this.authService.user!.token));
.subscribe(dto => {
content = content.unpublish(this.userToken, dto.version);
this.contentItems = this.contentItems.replaceBy('id', content);
this.emitContentUnpublished(content);
}, error => {
@ -142,8 +145,8 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
public archiveContent(content: ContentDto) {
this.appNameOnce()
.switchMap(app => this.contentsService.archiveContent(app, this.schema.name, content.id, content.version))
.subscribe(() => {
content = content.archive(this.authService.user!.token);
.subscribe(dto => {
content = content.archive(this.userToken, dto.version);
this.removeContent(content);
}, error => {
@ -154,8 +157,8 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
public restoreContent(content: ContentDto) {
this.appNameOnce()
.switchMap(app => this.contentsService.restoreContent(app, this.schema.name, content.id, content.version))
.subscribe(() => {
content = content.restore(this.authService.user!.token);
.subscribe(dto => {
content = content.restore(this.userToken, dto.version);
this.removeContent(content);
}, error => {

5
src/Squidex/app/features/content/pages/schemas/schemas-page.component.ts

@ -12,6 +12,7 @@ import { Observable } from 'rxjs';
import {
AppComponentBase,
AppsStoreService,
AuthService,
DialogService,
SchemaDto,
SchemasService
@ -52,10 +53,10 @@ export class SchemasPageComponent extends AppComponentBase {
});
});
constructor(apps: AppsStoreService, dialogs: DialogService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly schemasService: SchemasService
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
private loadSchemas(): Observable<SchemaDto[]> {

5
src/Squidex/app/features/content/shared/assets-editor.component.ts

@ -17,6 +17,7 @@ import {
AssetDto,
AssetsService,
AssetUpdated,
AuthService,
DialogService,
ImmutableArray,
MessageBus,
@ -43,11 +44,11 @@ export class AssetsEditorComponent extends AppComponentBase implements ControlVa
public isDisabled = false;
constructor(apps: AppsStoreService, dialogs: DialogService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly assetsService: AssetsService,
private readonly messageBus: MessageBus
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnInit() {

5
src/Squidex/app/features/content/shared/content-item.component.ts

@ -10,6 +10,7 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angu
import {
AppComponentBase,
AppsStoreService,
AuthService,
ContentDto,
DialogService,
fadeAnimation,
@ -66,8 +67,8 @@ export class ContentItemComponent extends AppComponentBase implements OnInit, On
public values: any[] = [];
constructor(apps: AppsStoreService, dialogs: DialogService) {
super(dialogs, apps);
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService) {
super(dialogs, apps, authService);
}
public ngOnChanges() {

5
src/Squidex/app/features/content/shared/references-editor.component.ts

@ -13,6 +13,7 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import {
AppComponentBase,
AppsStoreService,
AuthService,
ContentDto,
ContentsService,
DialogService,
@ -53,11 +54,11 @@ export class ReferencesEditorComponent extends AppComponentBase implements Contr
public isDisabled = false;
public isInvalidSchema = false;
constructor(apps: AppsStoreService, dialogs: DialogService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly contentsService: ContentsService,
private readonly schemasService: SchemasService
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnInit() {

5
src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts

@ -60,11 +60,10 @@ export class DashboardPageComponent extends AppComponentBase implements OnInit {
public callsCurrent = 0;
public callsMax = 0;
constructor(apps: AppsStoreService, dialogs: DialogService,
private readonly authService: AuthService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly usagesService: UsagesService
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnInit() {

58
src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts

@ -30,7 +30,8 @@ import {
SchemasService,
UpdateFieldDto,
UpdateSchemaScriptsDto,
ValidatorsEx
ValidatorsEx,
Version
} from 'shared';
import {
@ -82,15 +83,14 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
return this.addFieldForm.controls['name'].value && this.addFieldForm.controls['name'].value.length > 0;
}
constructor(apps: AppsStoreService, dialogs: DialogService,
private readonly authService: AuthService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly formBuilder: FormBuilder,
private readonly messageBus: MessageBus,
private readonly route: ActivatedRoute,
private readonly router: Router,
private readonly schemasService: SchemasService
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnDestroy() {
@ -129,8 +129,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
public publish() {
this.appNameOnce()
.switchMap(app => this.schemasService.publishSchema(app, this.schema.name, this.schema.version)).retry(2)
.subscribe(() => {
this.updateSchema(this.schema.publish(this.authService.user!.token));
.subscribe(dto => {
this.updateSchema(this.schema.publish(this.userToken, dto.version));
}, error => {
this.notifyError(error);
});
@ -139,8 +139,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
public unpublish() {
this.appNameOnce()
.switchMap(app => this.schemasService.unpublishSchema(app, this.schema.name, this.schema.version)).retry(2)
.subscribe(() => {
this.updateSchema(this.schema.unpublish(this.authService.user!.token));
.subscribe(dto => {
this.updateSchema(this.schema.unpublish(this.userToken, dto.version));
}, error => {
this.notifyError(error);
});
@ -149,8 +149,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
public enableField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.enableField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateSchema(this.schema.updateField(field.enable(), this.authService.user!.token));
.subscribe(dto => {
this.updateSchema(this.schema.updateField(field.enable(), this.userToken, dto.version));
}, error => {
this.notifyError(error);
});
@ -159,8 +159,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
public disableField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.disableField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateSchema(this.schema.updateField(field.disable(), this.authService.user!.token));
.subscribe(dto => {
this.updateSchema(this.schema.updateField(field.disable(), this.userToken, dto.version));
}, error => {
this.notifyError(error);
});
@ -169,8 +169,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
public lockField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.lockField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateSchema(this.schema.updateField(field.lock(), this.authService.user!.token));
.subscribe(dto => {
this.updateSchema(this.schema.updateField(field.lock(), this.userToken, dto.version));
}, error => {
this.notifyError(error);
});
@ -179,8 +179,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
public showField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.showField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateSchema(this.schema.updateField(field.show(), this.authService.user!.token));
.subscribe(dto => {
this.updateSchema(this.schema.updateField(field.show(), this.userToken, dto.version));
}, error => {
this.notifyError(error);
});
@ -189,8 +189,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
public hideField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.hideField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateSchema(this.schema.updateField(field.hide(), this.authService.user!.token));
.subscribe(dto => {
this.updateSchema(this.schema.updateField(field.hide(), this.userToken, dto.version));
}, error => {
this.notifyError(error);
});
@ -199,8 +199,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
public deleteField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.deleteField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateSchema(this.schema.removeField(field, this.authService.user!.token));
.subscribe(dto => {
this.updateSchema(this.schema.removeField(field, this.userToken, dto.version));
}, error => {
this.notifyError(error);
});
@ -209,8 +209,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
public sortFields(fields: FieldDto[]) {
this.appNameOnce()
.switchMap(app => this.schemasService.putFieldOrdering(app, this.schema.name, fields.map(t => t.fieldId), this.schema.version)).retry(2)
.subscribe(() => {
this.updateSchema(this.schema.replaceFields(fields, this.authService.user!.token));
.subscribe(dto => {
this.updateSchema(this.schema.replaceFields(fields, this.userToken, dto.version));
}, error => {
this.notifyError(error);
});
@ -221,8 +221,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
this.appNameOnce()
.switchMap(app => this.schemasService.putField(app, this.schema.name, field.fieldId, requestDto, this.schema.version)).retry(2)
.subscribe(() => {
this.updateSchema(this.schema.updateField(field, this.authService.user!.token));
.subscribe(dto => {
this.updateSchema(this.schema.updateField(field, this.userToken, dto.version));
}, error => {
this.notifyError(error);
});
@ -253,7 +253,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
this.appNameOnce()
.switchMap(app => this.schemasService.postField(app, this.schema.name, requestDto, this.schema.version))
.subscribe(dto => {
this.updateSchema(this.schema.addField(dto, this.authService.user!.token));
this.updateSchema(this.schema.addField(dto.payload, this.userToken, dto.version));
this.resetFieldForm();
}, error => {
this.notifyError(error);
@ -266,14 +266,14 @@ export class SchemaPageComponent extends AppComponentBase implements OnDestroy,
this.resetFieldForm();
}
public onSchemaSaved(properties: SchemaPropertiesDto) {
this.updateSchema(this.schema.update(properties, this.authService.user!.token));
public onSchemaSaved(properties: SchemaPropertiesDto, version: Version) {
this.updateSchema(this.schema.update(properties, this.userToken, version));
this.editSchemaDialog.hide();
}
public onSchemaScriptsSaved(scripts: UpdateSchemaScriptsDto) {
this.updateSchema(this.schema.configureScripts(scripts, this.authService.user!.token));
public onSchemaScriptsSaved(scripts: UpdateSchemaScriptsDto, version: Version) {
this.updateSchema(this.schema.configureScripts(scripts, this.userToken, version));
this.configureScriptsDialog.hide();
}

9
src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts

@ -15,8 +15,7 @@ import {
fadeAnimation,
SchemaDetailsDto,
SchemasService,
ValidatorsEx,
Version
ValidatorsEx
} from 'shared';
const FALLBACK_NAME = 'my-schema';
@ -83,14 +82,12 @@ export class SchemaFormComponent {
if (this.createForm.valid) {
this.createForm.disable();
const schemaVersion = new Version();
const schemaName = this.createForm.controls['name'].value;
const requestDto = Object.assign(this.createForm.controls['import'].value || {}, { name: schemaName });
const schemaDto = Object.assign(this.createForm.controls['import'].value || {}, { name: schemaName });
const me = this.authService.user!.token;
this.schemas.postSchema(this.appName, requestDto, me, DateTime.now(), schemaVersion)
this.schemas.postSchema(this.appName, schemaDto, me, DateTime.now())
.subscribe(dto => {
this.emitCreated(dto);
this.resetCreateForm();

5
src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts

@ -13,6 +13,7 @@ import { Subscription } from 'rxjs';
import {
AppComponentBase,
AppsStoreService,
AuthService,
DialogService,
fadeAnimation,
ImmutableArray,
@ -47,12 +48,12 @@ export class SchemasPageComponent extends AppComponentBase implements OnDestroy,
public schemasFilter = new FormControl();
public schemasFiltered = ImmutableArray.empty<SchemaDto>();
constructor(apps: AppsStoreService, dialogs: DialogService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly schemasService: SchemasService,
private readonly messageBus: MessageBus,
private readonly route: ActivatedRoute
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnDestroy() {

6
src/Squidex/app/features/settings/pages/clients/clients-page.component.html

@ -13,18 +13,18 @@
<div class="panel-main">
<div class="panel-content panel-content-scroll">
<div class="table-items-row table-items-row-empty" *ngIf="appClients && appClients.length === 0">
<div class="table-items-row table-items-row-empty" *ngIf="appClients && appClients.clients.length === 0">
No client created yet.
</div>
<div *ngFor="let client of appClients">
<div *ngFor="let client of appClients?.clients">
<sqx-client [client]="client" [appName]="appName() | async"
(changing)="changeClient(client, $event)"
(renaming)="renameClient(client, $event)"
(revoking)="revokeClient(client)"></sqx-client>
</div>
<div class="table-items-footer">
<div class="table-items-footer" *ngIf="appClients">
<form [formGroup]="addClientForm" (ngSubmit)="attachClient()">
<div class="row no-gutters">
<div class="col">

44
src/Squidex/app/features/settings/pages/clients/clients-page.component.ts

@ -10,17 +10,17 @@ import { FormBuilder, Validators } from '@angular/forms';
import {
AppClientDto,
AppClientsDto,
AppClientsService,
AppComponentBase,
AppsStoreService,
AuthService,
CreateAppClientDto,
DialogService,
HistoryChannelUpdated,
ImmutableArray,
MessageBus,
UpdateAppClientDto,
ValidatorsEx,
Version
ValidatorsEx
} from 'shared';
@Component({
@ -29,9 +29,7 @@ import {
templateUrl: './clients-page.component.html'
})
export class ClientsPageComponent extends AppComponentBase implements OnInit {
private version = new Version();
public appClients: ImmutableArray<AppClientDto>;
public appClients: AppClientsDto;
public addClientFormSubmitted = false;
public addClientForm =
@ -47,12 +45,12 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
return this.addClientForm.controls['name'].value && this.addClientForm.controls['name'].value.length > 0;
}
constructor(apps: AppsStoreService, dialogs: DialogService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly appClientsService: AppClientsService,
private readonly messageBus: MessageBus,
private readonly formBuilder: FormBuilder
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnInit() {
@ -61,9 +59,9 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
public load() {
this.appNameOnce()
.switchMap(app => this.appClientsService.getClients(app, this.version).retry(2))
.switchMap(app => this.appClientsService.getClients(app).retry(2))
.subscribe(dtos => {
this.updateClients(ImmutableArray.of(dtos));
this.updateClients(dtos);
}, error => {
this.notifyError(error);
});
@ -71,9 +69,9 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
public revokeClient(client: AppClientDto) {
this.appNameOnce()
.switchMap(app => this.appClientsService.deleteClient(app, client.id, this.version))
.subscribe(() => {
this.updateClients(this.appClients.remove(client));
.switchMap(app => this.appClientsService.deleteClient(app, client.id, this.appClients.version))
.subscribe(dto => {
this.updateClients(this.appClients.removeClient(client, dto.version));
}, error => {
this.notifyError(error);
});
@ -83,9 +81,9 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
const requestDto = new UpdateAppClientDto(name);
this.appNameOnce()
.switchMap(app => this.appClientsService.updateClient(app, client.id, requestDto, this.version))
.subscribe(() => {
this.updateClients(this.appClients.replaceBy('id', client.rename(name)));
.switchMap(app => this.appClientsService.updateClient(app, client.id, requestDto, this.appClients.version))
.subscribe(dto => {
this.updateClients(this.appClients.updateClient(client.rename(name), dto.version));
}, error => {
this.notifyError(error);
});
@ -95,9 +93,9 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
const requestDto = new UpdateAppClientDto(undefined, isReader);
this.appNameOnce()
.switchMap(app => this.appClientsService.updateClient(app, client.id, requestDto, this.version))
.subscribe(() => {
this.updateClients(this.appClients.replaceBy('id', client.change(isReader)));
.switchMap(app => this.appClientsService.updateClient(app, client.id, requestDto, this.appClients.version))
.subscribe(dto => {
this.updateClients(this.appClients.updateClient(client.change(isReader), dto.version));
}, error => {
this.notifyError(error);
});
@ -112,9 +110,9 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
const requestDto = new CreateAppClientDto(this.addClientForm.controls['name'].value);
this.appNameOnce()
.switchMap(app => this.appClientsService.postClient(app, requestDto, this.version))
.switchMap(app => this.appClientsService.postClient(app, requestDto, this.appClients.version))
.subscribe(dto => {
this.updateClients(this.appClients.push(dto));
this.updateClients(this.appClients.addClient(dto.payload, dto.version));
}, error => {
this.notifyError(error);
}, () => {
@ -133,8 +131,8 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
this.addClientForm.reset();
}
private updateClients(clients: ImmutableArray<AppClientDto>) {
this.appClients = clients;
private updateClients(appClients: AppClientsDto) {
this.appClients = appClients;
this.messageBus.emit(new HistoryChannelUpdated());
}

4
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html

@ -27,7 +27,7 @@
</colgroup>
<tbody>
<ng-template ngFor let-contributor [ngForOf]="appContributors">
<ng-template ngFor let-contributor [ngForOf]="appContributors?.contributors">
<tr>
<td>
<img class="user-picture" [attr.title]="contributor.contributorId | sqxUserName" [attr.src]="contributor.contributorId | sqxUserPicture" />
@ -54,7 +54,7 @@
</tbody>
</table>
<div class="table-items-footer">
<div class="table-items-footer" *ngIf="appContributors">
<form [formGroup]="addContributorForm" (ngSubmit)="assignContributor()">
<div class="row no-gutters">
<div class="col">

47
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts

@ -19,10 +19,8 @@ import {
AutocompleteSource,
DialogService,
HistoryChannelUpdated,
ImmutableArray,
MessageBus,
UsersService,
Version
UsersService
} from 'shared';
export class UsersDataSource implements AutocompleteSource {
@ -38,7 +36,7 @@ export class UsersDataSource implements AutocompleteSource {
const results: any[] = [];
for (let user of users) {
if (!this.component.appContributors || !this.component.appContributors.find(t => t.contributorId === user.id)) {
if (!this.component.appContributors || !this.component.appContributors.contributors.find(t => t.contributorId === user.id)) {
results.push(user);
}
}
@ -53,9 +51,7 @@ export class UsersDataSource implements AutocompleteSource {
templateUrl: './contributors-page.component.html'
})
export class ContributorsPageComponent extends AppComponentBase implements OnInit {
private version = new Version();
public appContributors = ImmutableArray.empty<AppContributorDto>();
public appContributors: AppContributorsDto;
public currentUserId: string;
@ -70,7 +66,7 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni
];
public get canAddContributor() {
return this.addContributorForm.valid && (this.maxContributors <= -1 || this.appContributors.length < this.maxContributors);
return this.addContributorForm.valid && (this.maxContributors <= -1 || this.appContributors.contributors.length < this.maxContributors);
}
public addContributorForm =
@ -81,13 +77,12 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni
]]
});
constructor(apps: AppsStoreService, dialogs: DialogService, usersService: UsersService,
constructor(apps: AppsStoreService, dialogs: DialogService, usersService: UsersService, authService: AuthService,
private readonly appContributorsService: AppContributorsService,
private readonly messageBus: MessageBus,
private readonly authService: AuthService,
private readonly formBuilder: FormBuilder
) {
super(dialogs, apps);
super(dialogs, apps, authService);
this.usersDataSource = new UsersDataSource(usersService, this);
}
@ -100,7 +95,7 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni
public load() {
this.appNameOnce()
.switchMap(app => this.appContributorsService.getContributors(app, this.version).retry(2))
.switchMap(app => this.appContributorsService.getContributors(app).retry(2))
.subscribe(dto => {
this.updateContributorsFromDto(dto);
}, error => {
@ -110,9 +105,9 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni
public removeContributor(contributor: AppContributorDto) {
this.appNameOnce()
.switchMap(app => this.appContributorsService.deleteContributor(app, contributor.contributorId, this.version))
.subscribe(() => {
this.updateContributors(this.appContributors.remove(contributor));
.switchMap(app => this.appContributorsService.deleteContributor(app, contributor.contributorId, this.appContributors.version))
.subscribe(dto => {
this.updateContributors(this.appContributors.removeContributor(contributor, dto.version));
}, error => {
this.notifyError(error);
});
@ -122,9 +117,9 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni
const requestDto = contributor.changePermission(permission);
this.appNameOnce()
.switchMap(app => this.appContributorsService.postContributor(app, requestDto, this.version))
.subscribe(() => {
this.updateContributors(this.appContributors.replace(contributor, requestDto));
.switchMap(app => this.appContributorsService.postContributor(app, requestDto, this.appContributors.version))
.subscribe(dto => {
this.updateContributors(this.appContributors.updateContributor(contributor, dto.version));
}, error => {
this.notifyError(error);
});
@ -134,9 +129,9 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni
const requestDto = new AppContributorDto(this.addContributorForm.controls['user'].value.id, 'Editor');
this.appNameOnce()
.switchMap(app => this.appContributorsService.postContributor(app, requestDto, this.version))
.subscribe(() => {
this.updateContributors(this.appContributors.push(requestDto));
.switchMap(app => this.appContributorsService.postContributor(app, requestDto, this.appContributors.version))
.subscribe(dto => {
this.updateContributors(this.appContributors.addContributor(requestDto, dto.version));
this.resetContributorForm();
}, error => {
this.notifyError(error);
@ -148,14 +143,14 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni
this.addContributorForm.reset();
}
private updateContributorsFromDto(dto: AppContributorsDto) {
this.updateContributors(ImmutableArray.of(dto.contributors));
private updateContributorsFromDto(appContributors: AppContributorsDto) {
this.updateContributors(appContributors);
this.maxContributors = dto.maxContributors;
this.maxContributors = appContributors.maxContributors;
}
private updateContributors(contributors: ImmutableArray<AppContributorDto>) {
this.appContributors = contributors;
private updateContributors(appContributors: AppContributorsDto) {
this.appContributors = appContributors;
this.messageBus.emit(new HistoryChannelUpdated());
}

14
src/Squidex/app/features/settings/pages/languages/language.component.ts

@ -9,11 +9,7 @@ import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output, OnInit }
import { FormBuilder } from '@angular/forms';
import { Subscription } from 'rxjs';
import {
AppLanguageDto,
fadeAnimation,
ImmutableArray
} from 'shared';
import { AppLanguageDto, fadeAnimation } from 'shared';
@Component({
selector: 'sqx-language',
@ -30,7 +26,7 @@ export class LanguageComponent implements OnInit, OnChanges, OnDestroy {
public language: AppLanguageDto;
@Input()
public allLanguages: ImmutableArray<AppLanguageDto>;
public allLanguages: AppLanguageDto[];
@Output()
public removing = new EventEmitter<AppLanguageDto>();
@ -38,7 +34,7 @@ export class LanguageComponent implements OnInit, OnChanges, OnDestroy {
@Output()
public saving = new EventEmitter<AppLanguageDto>();
public otherLanguages: ImmutableArray<AppLanguageDto>;
public otherLanguages: AppLanguageDto[];
public otherLanguage: AppLanguageDto;
public fallbackLanguages: AppLanguageDto[] = [];
@ -92,7 +88,7 @@ export class LanguageComponent implements OnInit, OnChanges, OnDestroy {
public removeFallbackLanguage(language: AppLanguageDto) {
this.fallbackLanguages.splice(this.fallbackLanguages.indexOf(language), 1);
this.otherLanguages = this.otherLanguages.push(language);
this.otherLanguages = [...this.otherLanguages, language];
this.otherLanguage = this.otherLanguages.values[0];
}
@ -142,7 +138,7 @@ export class LanguageComponent implements OnInit, OnChanges, OnDestroy {
this.fallbackLanguages =
this.allLanguages.filter(l =>
this.language.fallback.indexOf(l.iso2Code) >= 0).values;
this.language.fallback.indexOf(l.iso2Code) >= 0);
}
}
}

6
src/Squidex/app/features/settings/pages/languages/languages-page.component.html

@ -13,13 +13,13 @@
<div class="panel-main">
<div class="panel-content panel-content-scroll">
<div *ngFor="let language of appLanguages">
<sqx-language [language]="language" [allLanguages]="appLanguages"
<div *ngFor="let language of appLanguages?.languages">
<sqx-language [language]="language" [allLanguages]="appLanguages.languages"
(saving)="updateLanguage($event)"
(removing)="removeLanguage($event)"></sqx-language>
</div>
<div class="table-items-footer">
<div class="table-items-footer" *ngIf="appLanguages">
<form [formGroup]="addLanguageForm" (ngSubmit)="addLanguage()">
<div class="row no-gutters">
<div class="col">

76
src/Squidex/app/features/settings/pages/languages/languages-page.component.ts

@ -12,15 +12,16 @@ import {
AddAppLanguageDto,
AppComponentBase,
AppLanguageDto,
AppLanguagesDto,
AppLanguagesService,
AppsStoreService,
AuthService,
DialogService,
HistoryChannelUpdated,
ImmutableArray,
MessageBus,
LanguageDto,
LanguagesService,
Version
LanguagesService
} from 'shared';
@Component({
@ -29,11 +30,9 @@ import {
templateUrl: './languages-page.component.html'
})
export class LanguagesPageComponent extends AppComponentBase implements OnInit {
private version = new Version();
public allLanguages: LanguageDto[] = [];
public newLanguages: LanguageDto[] = [];
public appLanguages = ImmutableArray.empty<AppLanguageDto>();
public appLanguages: AppLanguagesDto;
public addLanguageForm =
this.formBuilder.group({
@ -42,13 +41,13 @@ export class LanguagesPageComponent extends AppComponentBase implements OnInit {
]
});
constructor(apps: AppsStoreService, dialogs: DialogService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly appLanguagesService: AppLanguagesService,
private readonly languagesService: LanguagesService,
private readonly messageBus: MessageBus,
private readonly formBuilder: FormBuilder
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnInit() {
@ -58,9 +57,9 @@ export class LanguagesPageComponent extends AppComponentBase implements OnInit {
public load() {
this.appNameOnce()
.switchMap(app => this.appLanguagesService.getLanguages(app, this.version).retry(2))
.subscribe(dtos => {
this.updateLanguages(ImmutableArray.of(dtos));
.switchMap(app => this.appLanguagesService.getLanguages(app).retry(2))
.subscribe(dto => {
this.updateLanguages(dto);
}, error => {
this.notifyError(error);
});
@ -68,9 +67,9 @@ export class LanguagesPageComponent extends AppComponentBase implements OnInit {
public removeLanguage(language: AppLanguageDto) {
this.appNameOnce()
.switchMap(app => this.appLanguagesService.deleteLanguage(app, language.iso2Code, this.version))
.switchMap(app => this.appLanguagesService.deleteLanguage(app, language.iso2Code, this.appLanguages.version))
.subscribe(dto => {
this.updateLanguages(this.appLanguages.remove(language));
this.updateLanguages(this.appLanguages.removeLanguage(language, dto.version));
}, error => {
this.notifyError(error);
});
@ -80,9 +79,9 @@ export class LanguagesPageComponent extends AppComponentBase implements OnInit {
const requestDto = new AddAppLanguageDto(this.addLanguageForm.controls['language'].value.iso2Code);
this.appNameOnce()
.switchMap(app => this.appLanguagesService.postLanguages(app, requestDto, this.version))
.switchMap(app => this.appLanguagesService.postLanguages(app, requestDto, this.appLanguages.version))
.subscribe(dto => {
this.updateLanguages(this.appLanguages.push(dto));
this.updateLanguages(this.appLanguages.addLanguage(dto.payload, dto.version));
}, error => {
this.notifyError(error);
});
@ -90,13 +89,9 @@ export class LanguagesPageComponent extends AppComponentBase implements OnInit {
public updateLanguage(language: AppLanguageDto) {
this.appNameOnce()
.switchMap(app => this.appLanguagesService.updateLanguage(app, language.iso2Code, language, this.version))
.switchMap(app => this.appLanguagesService.updateLanguage(app, language.iso2Code, language, this.appLanguages.version))
.subscribe(dto => {
this.updateLanguages(
this.appLanguages.replaceAll(
l => l.iso2Code === language.iso2Code,
l => language),
language.isMaster ? language.iso2Code : undefined);
this.updateLanguages(this.appLanguages.updateLanguage(language, dto.version));
}, error => {
this.notifyError(error);
});
@ -113,26 +108,27 @@ export class LanguagesPageComponent extends AppComponentBase implements OnInit {
});
}
private updateLanguages(languages: ImmutableArray<AppLanguageDto>, masterId?: string) {
private updateLanguages(appLanguages: AppLanguagesDto, masterId?: string) {
this.addLanguageForm.reset();
this.appLanguages =
languages.map(l => {
const isMaster = masterId ? l.iso2Code === masterId : l.isMaster;
return new AppLanguageDto(
l.iso2Code,
l.englishName, isMaster,
l.isOptional,
l.fallback.filter(f => !!languages.find(l2 => l2.iso2Code === f))
);
}).sort((a, b) => {
if (a.isMaster === b.isMaster) {
return a.iso2Code.localeCompare(b.iso2Code);
} else {
return (a.isMaster ? 0 : 1) - (b.isMaster ? 0 : 1);
}
});
new AppLanguagesDto(
appLanguages.languages.map(l => {
const isMaster = masterId ? l.iso2Code === masterId : l.isMaster;
return new AppLanguageDto(
l.iso2Code,
l.englishName, isMaster,
l.isOptional,
l.fallback.filter(f => !!appLanguages.languages.find(l2 => l2.iso2Code === f))
);
}).sort((a, b) => {
if (a.isMaster === b.isMaster) {
return a.iso2Code.localeCompare(b.iso2Code);
} else {
return (a.isMaster ? 0 : 1) - (b.isMaster ? 0 : 1);
}
}), appLanguages.version);
this.updateNewLanguages();
@ -140,8 +136,10 @@ export class LanguagesPageComponent extends AppComponentBase implements OnInit {
}
private updateNewLanguages() {
this.newLanguages = this.allLanguages.filter(x => !this.appLanguages.find(l => l.iso2Code === x.iso2Code));
this.addLanguageForm.controls['language'].setValue(this.newLanguages[0]);
if (this.appLanguages) {
this.newLanguages = this.allLanguages.filter(x => !this.appLanguages.languages.find(l => l.iso2Code === x.iso2Code));
this.addLanguageForm.controls['language'].setValue(this.newLanguages[0]);
}
}
}

29
src/Squidex/app/features/settings/pages/plans/plans-page.component.ts

@ -17,8 +17,7 @@ import {
AuthService,
ChangePlanDto,
DialogService,
PlansService,
Version
PlansService
} from 'shared';
@Component({
@ -29,7 +28,6 @@ import {
export class PlansPageComponent extends AppComponentBase implements OnDestroy, OnInit {
private queryParamsSubscription: Subscription;
private overridePlanId: string;
private version = new Version();
public portalUrl = this.apiUrl.buildUrl('/identity-server/account/portal');
@ -38,13 +36,12 @@ export class PlansPageComponent extends AppComponentBase implements OnDestroy, O
public isDisabled = false;
constructor(apps: AppsStoreService, dialogs: DialogService,
private readonly authService: AuthService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly plansService: PlansService,
private readonly route: ActivatedRoute,
private readonly apiUrl: ApiUrlConfig
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnDestroy() {
@ -62,14 +59,10 @@ export class PlansPageComponent extends AppComponentBase implements OnDestroy, O
public load(showInfo = false) {
this.appNameOnce()
.switchMap(app => this.plansService.getPlans(app, this.version).retry(2))
.switchMap(app => this.plansService.getPlans(app).retry(2))
.subscribe(dto => {
if (this.overridePlanId) {
this.plans = new AppPlansDto(
this.overridePlanId,
dto.planOwner,
dto.hasPortal,
dto.plans);
this.plans = dto.changePlanId(this.overridePlanId);
} else {
this.plans = dto;
}
@ -88,16 +81,12 @@ export class PlansPageComponent extends AppComponentBase implements OnDestroy, O
this.isDisabled = true;
this.appNameOnce()
.switchMap(app => this.plansService.putPlan(app, new ChangePlanDto(planId), this.version))
.switchMap(app => this.plansService.putPlan(app, new ChangePlanDto(planId), this.plans.version))
.subscribe(dto => {
if (dto.redirectUri && dto.redirectUri.length > 0) {
window.location.href = dto.redirectUri;
if (dto.payload.redirectUri && dto.payload.redirectUri.length > 0) {
window.location.href = dto.payload.redirectUri;
} else {
this.plans =
new AppPlansDto(planId,
this.plans.planOwner,
this.plans.hasPortal,
this.plans.plans);
this.plans = this.plans.changePlanId(planId, dto.version);
this.isDisabled = false;
}
}, error => {

5
src/Squidex/app/features/settings/settings-area.component.ts

@ -10,6 +10,7 @@ import { Component } from '@angular/core';
import {
AppComponentBase,
AppsStoreService,
AuthService,
DialogService
} from 'shared';
@ -19,8 +20,8 @@ import {
templateUrl: './settings-area.component.html'
})
export class SettingsAreaComponent extends AppComponentBase {
constructor(apps: AppsStoreService, dialogs: DialogService
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
}

7
src/Squidex/app/features/webhooks/pages/webhook-events-page.component.ts

@ -10,6 +10,7 @@ import { Component, OnInit } from '@angular/core';
import {
AppComponentBase,
AppsStoreService,
AuthService,
DialogService,
ImmutableArray,
Pager,
@ -28,10 +29,10 @@ export class WebhookEventsPageComponent extends AppComponentBase implements OnIn
public selectedEventId: string | null = null;
constructor(dialogs: DialogService, appsStore: AppsStoreService,
constructor(dialogs: DialogService, appsStore: AppsStoreService, authService: AuthService,
private readonly webhooksService: WebhooksService
) {
super(dialogs, appsStore);
super(dialogs, appsStore, authService);
}
public ngOnInit() {
@ -56,7 +57,7 @@ export class WebhookEventsPageComponent extends AppComponentBase implements OnIn
public enqueueEvent(event: WebhookEventDto) {
this.appNameOnce()
.switchMap(app => this.webhooksService.enqueueEvent(app, event.id))
.subscribe(dtos => {
.subscribe(() => {
this.notifyInfo('Events enqueued. Will be send in a few seconds.');
}, error => {
this.notifyError(error);

12
src/Squidex/app/features/webhooks/pages/webhooks-page.component.ts

@ -18,7 +18,6 @@ import {
ImmutableArray,
SchemaDto,
SchemasService,
Version,
WebhookDto,
WebhooksService,
UpdateWebhookDto
@ -46,13 +45,12 @@ export class WebhooksPageComponent extends AppComponentBase implements OnInit {
return this.addWebhookForm.controls['url'].value && this.addWebhookForm.controls['url'].value.length > 0;
}
constructor(apps: AppsStoreService, dialogs: DialogService,
private readonly authService: AuthService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly schemasService: SchemasService,
private readonly webhooksService: WebhooksService,
private readonly formBuilder: FormBuilder
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnInit() {
@ -91,7 +89,7 @@ export class WebhooksPageComponent extends AppComponentBase implements OnInit {
this.appNameOnce()
.switchMap(app => this.webhooksService.putWebhook(app, webhook.id, requestDto, webhook.version))
.subscribe(dto => {
this.webhooks = this.webhooks.replace(webhook, webhook.update(requestDto, this.authService.user!.token));
this.webhooks = this.webhooks.replace(webhook, webhook.update(requestDto, this.userToken, dto.version));
this.notifyInfo('Webhook saved.');
}, error => {
@ -107,10 +105,10 @@ export class WebhooksPageComponent extends AppComponentBase implements OnInit {
const requestDto = new CreateWebhookDto(this.addWebhookForm.controls['url'].value, []);
const me = this.authService.user!.token;
const me = this.userToken;
this.appNameOnce()
.switchMap(app => this.webhooksService.postWebhook(app, requestDto, me, DateTime.now(), new Version()))
.switchMap(app => this.webhooksService.postWebhook(app, requestDto, me, DateTime.now()))
.subscribe(dto => {
this.webhooks = this.webhooks.push(dto);

40
src/Squidex/app/framework/angular/http-extensions-impl.ts

@ -10,6 +10,14 @@ import { Observable } from 'rxjs';
import { Version } from './../utils/version';
export class Versioned<T> {
constructor(
public readonly version: Version,
public readonly payload: T
) {
}
}
export class ErrorDto {
public get displayMessage(): string {
let result = this.message;
@ -46,28 +54,28 @@ export class ErrorDto {
}
export module HTTP {
export function getVersioned(http: HttpClient, url: string, version?: Version): Observable<any> {
export function getVersioned<T>(http: HttpClient, url: string, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.get(url, { observe: 'response', headers }), version);
return handleVersion(http.get<T>(url, { observe: 'response', headers }), version);
}
export function postVersioned(http: HttpClient, url: string, body: any, version?: Version): Observable<any> {
export function postVersioned<T>(http: HttpClient, url: string, body: any, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.post(url, body, { observe: 'response', headers }), version);
return handleVersion(http.post<T>(url, body, { observe: 'response', headers }), version);
}
export function putVersioned(http: HttpClient, url: string, body: any, version?: Version): Observable<any> {
export function putVersioned<T>(http: HttpClient, url: string, body: any, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.put(url, body, { observe: 'response', headers }), version);
return handleVersion(http.put<T>(url, body, { observe: 'response', headers }), version);
}
export function deleteVersioned(http: HttpClient, url: string, version?: Version): Observable<any> {
export function deleteVersioned<T>(http: HttpClient, url: string, version?: Version): Observable<Versioned<HttpResponse<T>>> {
const headers = createHeaders(version);
return handleVersion(http.delete(url, { observe: 'response', headers }), version);
return handleVersion(http.delete<T>(url, { observe: 'response', headers }), version);
}
function createHeaders(version?: Version): HttpHeaders {
@ -78,16 +86,12 @@ export module HTTP {
}
}
function handleVersion(httpRequest: Observable<HttpResponse<any>>, version?: Version): Observable<any> {
return httpRequest.do((response: HttpResponse<any>) => {
if (version && response.status.toString().indexOf('2') === 0 && response.headers) {
const etag = response.headers.get('etag');
function handleVersion<T>(httpRequest: Observable<HttpResponse<T>>, version?: Version): Observable<Versioned<HttpResponse<T>>> {
return httpRequest.map((response: HttpResponse<T>) => {
const etag = response.headers.get('etag') || '';
if (etag) {
version.update(etag);
}
}
}).map((response: HttpResponse<any>) => response.body);
return new Versioned(new Version(etag), response);
});
}
}
@ -105,7 +109,7 @@ export function pretifyError(message: string): Observable<any> {
result = new ErrorDto(response.status, errorDto.message, errorDto.details);
}
} catch (e) {
/* Ignore */
result = new ErrorDto(500, 'Failed to make the request.');
}
}

14
src/Squidex/app/framework/utils/version.spec.ts

@ -8,23 +8,9 @@
import { Version } from './../';
describe('Version', () => {
it('should initialize with default value', () => {
const version = new Version();
expect(version.value).toBe('');
});
it('should initialize with init value', () => {
const version = new Version('1.0');
expect(version.value).toBe('1.0');
});
it('should update to new value', () => {
const version = new Version();
version.update('1.0');
expect(version.value).toBe('1.0');
});
});

10
src/Squidex/app/framework/utils/version.ts

@ -6,16 +6,8 @@
*/
export class Version {
public get value() {
return this.currentValue;
}
constructor(
private currentValue: string = ''
public readonly value?: string
) {
}
public update(newValue: string) {
this.currentValue = newValue;
}
}

13
src/Squidex/app/shared/components/app.component-base.ts

@ -7,15 +7,24 @@
import { Observable } from 'rxjs';
import { AppsStoreService, DialogService } from './../declarations-base';
import {
AppsStoreService,
AuthService,
DialogService
} from './../declarations-base';
import { ComponentBase } from './component-base';
export abstract class AppComponentBase extends ComponentBase {
private appName$: Observable<string>;
public get userToken(): string {
return this.authService.user!.token;
}
constructor(dialogs: DialogService,
private readonly appsStore: AppsStoreService
protected readonly appsStore: AppsStoreService,
protected readonly authService: AuthService
) {
super(dialogs);

21
src/Squidex/app/shared/components/asset.component.ts

@ -13,7 +13,6 @@ import { AppComponentBase } from './app.component-base';
import {
AppsStoreService,
AssetDto,
AssetReplacedDto,
AssetsService,
AuthService,
DateTime,
@ -21,7 +20,8 @@ import {
fadeAnimation,
ModalView,
UpdateAssetDto,
Version
Version,
Versioned
} from './../declarations-base';
@Component({
@ -71,12 +71,11 @@ export class AssetComponent extends AppComponentBase implements OnInit {
public progress = 0;
constructor(apps: AppsStoreService, dialogs: DialogService,
constructor(apps: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly formBuilder: FormBuilder,
private readonly assetsService: AssetsService,
private readonly authService: AuthService
private readonly assetsService: AssetsService
) {
super(dialogs, apps);
super(dialogs, apps, authService);
}
public ngOnInit() {
@ -84,7 +83,7 @@ export class AssetComponent extends AppComponentBase implements OnInit {
if (initFile) {
this.appNameOnce()
.switchMap(app => this.assetsService.uploadFile(app, initFile, this.authService.user!.token, DateTime.now()))
.switchMap(app => this.assetsService.uploadFile(app, initFile, this.userToken, DateTime.now()))
.subscribe(dto => {
if (dto instanceof AssetDto) {
this.emitLoaded(dto);
@ -104,8 +103,8 @@ export class AssetComponent extends AppComponentBase implements OnInit {
this.appNameOnce()
.switchMap(app => this.assetsService.replaceFile(app, this.asset.id, files[0], this.assetVersion))
.subscribe(dto => {
if (dto instanceof AssetReplacedDto) {
this.updateAsset(this.asset.update(dto, this.authService.user!.token), true);
if (dto instanceof Versioned) {
this.updateAsset(this.asset.update(dto.payload, this.userToken, dto.version), true);
} else {
this.setProgress(dto);
}
@ -126,8 +125,8 @@ export class AssetComponent extends AppComponentBase implements OnInit {
this.appNameOnce()
.switchMap(app => this.assetsService.putAsset(app, this.asset.id, requestDto, this.assetVersion))
.subscribe(() => {
this.updateAsset(this.asset.rename(requestDto.fileName, this.authService.user!.token), true);
.subscribe(dto => {
this.updateAsset(this.asset.rename(requestDto.fileName, this.userToken, dto.version), true);
this.resetRenameForm();
}, error => {
this.notifyError(error);

5
src/Squidex/app/shared/components/history.component.ts

@ -14,6 +14,7 @@ import { AppComponentBase } from './app.component-base';
import {
allParams,
AppsStoreService,
AuthService,
DialogService,
HistoryChannelUpdated,
HistoryEventDto,
@ -54,13 +55,13 @@ export class HistoryComponent extends AppComponentBase {
.switchMap(() => this.appNameOnce())
.switchMap(app => this.historyService.getHistory(app, this.channel).retry(2));
constructor(appsStore: AppsStoreService, dialogs: DialogService,
constructor(appsStore: AppsStoreService, dialogs: DialogService, authService: AuthService,
private readonly users: UsersProviderService,
private readonly historyService: HistoryService,
private readonly messageBus: MessageBus,
private readonly route: ActivatedRoute
) {
super(dialogs, appsStore);
super(dialogs, appsStore, authService);
}
private userName(userId: string): Observable<string> {

8
src/Squidex/app/shared/guards/resolve-app-languages.guard.spec.ts

@ -8,7 +8,11 @@
import { IMock, Mock } from 'typemoq';
import { Observable } from 'rxjs';
import { AppLanguageDto, AppLanguagesService } from 'shared';
import {
AppLanguagesDto,
AppLanguagesService,
Version
} from 'shared';
import { ResolveAppLanguagesGuard } from './resolve-app-languages.guard';
import { RouterMockup } from './router-mockup';
@ -68,7 +72,7 @@ describe('ResolveAppLanguagesGuard', () => {
});
it('should return languages if loading succeeded', (done) => {
const languages: AppLanguageDto[] = [];
const languages = new AppLanguagesDto([], new Version('2'));
appLanguagesService.setup(x => x.getLanguages('my-app'))
.returns(() => Observable.of(languages));

2
src/Squidex/app/shared/guards/resolve-app-languages.guard.ts

@ -31,7 +31,7 @@ export class ResolveAppLanguagesGuard implements Resolve<AppLanguageDto[] | null
}
const result =
this.appLanguagesService.getLanguages(appName)
this.appLanguagesService.getLanguages(appName).map(d => d.languages)
.do(dto => {
if (!dto) {
this.router.navigate(['/404']);

51
src/Squidex/app/shared/services/app-clients.service.spec.ts

@ -13,12 +13,45 @@ import {
AnalyticsService,
ApiUrlConfig,
AppClientDto,
AppClientsDto,
AppClientsService,
CreateAppClientDto,
UpdateAppClientDto,
Version
} from './../';
describe('AppClientsDto', () => {
const client1 = new AppClientDto('1', '1', '1', false);
const client2 = new AppClientDto('2', '2', '1', false);
const client2_new = new AppClientDto('2', '2 New', '1 New', false);
const version = new Version('1');
const newVersion = new Version('2');
it('should update clients when adding client', () => {
const clients_1 = new AppClientsDto([client1], version);
const clients_2 = clients_1.addClient(client2, newVersion);
expect(clients_2.clients).toEqual([client1, client2]);
expect(clients_2.version).toEqual(newVersion);
});
it('should update clients when removing client', () => {
const clients_1 = new AppClientsDto([client1, client2], version);
const clients_2 = clients_1.removeClient(client1, newVersion);
expect(clients_2.clients).toEqual([client2]);
expect(clients_2.version).toEqual(newVersion);
});
it('should update clients when updating client', () => {
const clients_1 = new AppClientsDto([client1, client2], version);
const clients_2 = clients_1.updateClient(client2_new, newVersion);
expect(clients_2.clients).toEqual([client1, client2_new]);
expect(clients_2.version).toEqual(newVersion);
});
});
describe('AppClientDto', () => {
it('should update name property when renaming', () => {
const client_1 = new AppClientDto('1', 'old-name', 'secret', false);
@ -58,16 +91,16 @@ describe('AppClientsService', () => {
it('should make get request to get app clients',
inject([AppClientsService, HttpTestingController], (appClientsService: AppClientsService, httpMock: HttpTestingController) => {
let clients: AppClientDto[] | null = null;
let clients: AppClientsDto | null = null;
appClientsService.getClients('my-app', version).subscribe(result => {
appClientsService.getClients('my-app').subscribe(result => {
clients = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/clients');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toEqual(version.value);
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush([
{
@ -82,13 +115,17 @@ describe('AppClientsService', () => {
secret: 'secret2',
isReader: true
}
]);
], {
headers: {
etag: '2'
}
});
expect(clients).toEqual(
[
new AppClientsDto([
new AppClientDto('client1', 'Client 1', 'secret1', true),
new AppClientDto('client2', 'Client 2', 'secret2', true)
]);
], new Version('2')));
}));
it('should make post request to create client',
@ -99,7 +136,7 @@ describe('AppClientsService', () => {
let client: AppClientDto | null = null;
appClientsService.postClient('my-app', dto, version).subscribe(result => {
client = result;
client = result.payload;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/clients');

59
src/Squidex/app/shared/services/app-clients.service.ts

@ -15,9 +15,30 @@ import {
AnalyticsService,
ApiUrlConfig,
HTTP,
Version
Version,
Versioned
} from 'framework';
export class AppClientsDto {
constructor(
public readonly clients: AppClientDto[],
public readonly version: Version
) {
}
public addClient(client: AppClientDto, version: Version) {
return new AppClientsDto([...this.clients, client], version);
}
public updateClient(client: AppClientDto, version: Version) {
return new AppClientsDto(this.clients.map(c => c.id === client.id ? client : c), version);
}
public removeClient(client: AppClientDto, version: Version) {
return new AppClientsDto(this.clients.filter(c => c.id !== client.id), version);
}
}
export class AppClientDto {
constructor(
public readonly id: string,
@ -68,34 +89,42 @@ export class AppClientsService {
) {
}
public getClients(appName: string, version?: Version): Observable<AppClientDto[]> {
public getClients(appName: string): Observable<AppClientsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients`);
return HTTP.getVersioned(this.http, url, version)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
const clients = items.map(item => {
return new AppClientDto(
item.id,
item.name || response.id,
item.name || body.id,
item.secret,
item.isReader);
});
return new AppClientsDto(clients, response.version);
})
.pretifyError('Failed to load clients. Please reload.');
}
public postClient(appName: string, dto: CreateAppClientDto, version: Version): Observable<AppClientDto> {
public postClient(appName: string, dto: CreateAppClientDto, version: Version): Observable<Versioned<AppClientDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients`);
return HTTP.postVersioned(this.http, url, dto, version)
return HTTP.postVersioned<any>(this.http, url, dto, version)
.map(response => {
return new AppClientDto(
response.id,
response.name || response.id,
response.secret,
response.isReader);
const body = response.payload.body;
const client = new AppClientDto(
body.id,
body.name || body.id,
body.secret,
body.isReader);
return new Versioned(response.version, client);
})
.do(() => {
this.analytics.trackEvent('Client', 'Created', appName);
@ -103,7 +132,7 @@ export class AppClientsService {
.pretifyError('Failed to add client. Please reload.');
}
public updateClient(appName: string, id: string, dto: UpdateAppClientDto, version: Version): Observable<any> {
public updateClient(appName: string, id: string, dto: UpdateAppClientDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients/${id}`);
return HTTP.putVersioned(this.http, url, dto, version)
@ -113,7 +142,7 @@ export class AppClientsService {
.pretifyError('Failed to revoke client. Please reload.');
}
public deleteClient(appName: string, id: string, version: Version): Observable<any> {
public deleteClient(appName: string, id: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients/${id}`);
return HTTP.deleteVersioned(this.http, url, version)

46
src/Squidex/app/shared/services/app-contributors.service.spec.ts

@ -17,6 +17,38 @@ import {
Version
} from './../';
describe('AppContributorsDto', () => {
const contributor1 = new AppContributorDto('1', 'Owner');
const contributor2 = new AppContributorDto('2', 'Developer');
const contributor2_new = new AppContributorDto('2', 'Editor');
const version = new Version('1');
const newVersion = new Version('2');
it('should update contributors when adding contributor', () => {
const contributors_1 = new AppContributorsDto([contributor1], 4, version);
const contributors_2 = contributors_1.addContributor(contributor2, newVersion);
expect(contributors_2.contributors).toEqual([contributor1, contributor2]);
expect(contributors_2.version).toEqual(newVersion);
});
it('should update contributors when removing contributor', () => {
const contributors_1 = new AppContributorsDto([contributor1, contributor2], 4, version);
const contributors_2 = contributors_1.removeContributor(contributor1, newVersion);
expect(contributors_2.contributors).toEqual([contributor2]);
expect(contributors_2.version).toEqual(newVersion);
});
it('should update contributors when updating contributor', () => {
const contributors_1 = new AppContributorsDto([contributor1, contributor2], 4, version);
const contributors_2 = contributors_1.updateContributor(contributor2_new, newVersion);
expect(contributors_2.contributors).toEqual([contributor1, contributor2_new]);
expect(contributors_2.version).toEqual(newVersion);
});
});
describe('AppContributorDto', () => {
it('should update permission property when changing', () => {
const contributor_1 = new AppContributorDto('1', 'Owner');
@ -51,14 +83,14 @@ describe('AppContributorsService', () => {
let contributors: AppContributorsDto | null = null;
appContributorsService.getContributors('my-app', version).subscribe(result => {
appContributorsService.getContributors('my-app').subscribe(result => {
contributors = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/contributors');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toEqual(version.value);
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({
contributors: [
@ -72,13 +104,17 @@ describe('AppContributorsService', () => {
}
],
maxContributors: 100
}, {
headers: {
etag: '2'
}
});
expect(contributors).toEqual(
new AppContributorsDto([
new AppContributorDto('123', 'Owner'),
new AppContributorDto('456', 'Owner')
], 100));
new AppContributorDto('123', 'Owner'),
new AppContributorDto('456', 'Owner')
], 100, new Version('2')));
}));
it('should make post request to assign contributor',

52
src/Squidex/app/shared/services/app-contributors.service.ts

@ -15,27 +15,47 @@ import {
AnalyticsService,
ApiUrlConfig,
HTTP,
Version
Version,
Versioned
} from 'framework';
export class AppContributorDto {
export class AppContributorsDto {
constructor(
public readonly contributorId: string,
public readonly permission: string
public readonly contributors: AppContributorDto[],
public readonly maxContributors: number,
public readonly version: Version
) {
}
public changePermission(permission: string): AppContributorDto {
return new AppContributorDto(this.contributorId, permission);
public addContributor(contributor: AppContributorDto, version: Version) {
return new AppContributorsDto([...this.contributors, contributor], this.maxContributors, version);
}
public updateContributor(contributor: AppContributorDto, version: Version) {
return new AppContributorsDto(
this.contributors.map(c => c.contributorId === contributor.contributorId ? contributor : c),
this.maxContributors,
version);
}
public removeContributor(contributor: AppContributorDto, version: Version) {
return new AppContributorsDto(
this.contributors.filter(c => c.contributorId !== contributor.contributorId),
this.maxContributors,
version);
}
}
export class AppContributorsDto {
export class AppContributorDto {
constructor(
public readonly contributors: AppContributorDto[],
public readonly maxContributors: number
public readonly contributorId: string,
public readonly permission: string
) {
}
public changePermission(permission: string): AppContributorDto {
return new AppContributorDto(this.contributorId, permission);
}
}
@Injectable()
@ -47,12 +67,14 @@ export class AppContributorsService {
) {
}
public getContributors(appName: string, version?: Version): Observable<AppContributorsDto> {
public getContributors(appName: string): Observable<AppContributorsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors`);
return HTTP.getVersioned(this.http, url, version)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response.contributors;
const body = response.payload.body;
const items: any[] = body.contributors;
return new AppContributorsDto(
items.map(item => {
@ -60,12 +82,12 @@ export class AppContributorsService {
item.contributorId,
item.permission);
}),
response.maxContributors);
body.maxContributors, response.version);
})
.pretifyError('Failed to load contributors. Please reload.');
}
public postContributor(appName: string, dto: AppContributorDto, version: Version): Observable<any> {
public postContributor(appName: string, dto: AppContributorDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors`);
return HTTP.postVersioned(this.http, url, dto, version)
@ -75,7 +97,7 @@ export class AppContributorsService {
.pretifyError('Failed to add contributors. Please reload.');
}
public deleteContributor(appName: string, contributorId: string, version: Version): Observable<any> {
public deleteContributor(appName: string, contributorId: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors/${contributorId}`);
return HTTP.deleteVersioned(this.http, url, version)

51
src/Squidex/app/shared/services/app-languages.service.spec.ts

@ -13,11 +13,44 @@ import {
AnalyticsService,
ApiUrlConfig,
AppLanguageDto,
AppLanguagesDto,
AppLanguagesService,
UpdateAppLanguageDto,
Version
} from './../';
describe('AppLanguageDto', () => {
const language1 = new AppLanguageDto('de', 'English', false, false, []);
const language2 = new AppLanguageDto('en', 'English', false, false, []);
const language2_new = new AppLanguageDto('en', 'English (United States)', false, false, []);
const version = new Version('1');
const newVersion = new Version('2');
it('should update languages when adding language', () => {
const languages_1 = new AppLanguagesDto([language1], version);
const languages_2 = languages_1.addLanguage(language2, newVersion);
expect(languages_2.languages).toEqual([language1, language2]);
expect(languages_2.version).toEqual(newVersion);
});
it('should update languages when removing language', () => {
const languages_1 = new AppLanguagesDto([language1, language2], version);
const languages_2 = languages_1.removeLanguage(language1, newVersion);
expect(languages_2.languages).toEqual([language2]);
expect(languages_2.version).toEqual(newVersion);
});
it('should update languages when updating language', () => {
const languages_1 = new AppLanguagesDto([language1, language2], version);
const languages_2 = languages_1.updateLanguage(language2_new, newVersion);
expect(languages_2.languages).toEqual([language1, language2_new]);
expect(languages_2.version).toEqual(newVersion);
});
});
describe('AppLanguageDto', () => {
it('should update properties when updating', () => {
const language_1 = new AppLanguageDto('de', 'English', false, false, []);
@ -52,16 +85,16 @@ describe('AppLanguagesService', () => {
it('should make get request to get app languages',
inject([AppLanguagesService, HttpTestingController], (appLanguagesService: AppLanguagesService, httpMock: HttpTestingController) => {
let languages: AppLanguageDto[] | null = null;
let languages: AppLanguagesDto | null = null;
appLanguagesService.getLanguages('my-app', version).subscribe(result => {
appLanguagesService.getLanguages('my-app').subscribe(result => {
languages = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/languages');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toEqual(version.value);
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush([
{
@ -77,13 +110,17 @@ describe('AppLanguagesService', () => {
isOptional: false,
englishName: 'Italian'
}
]);
], {
headers: {
etag: '2'
}
});
expect(languages).toEqual(
[
new AppLanguagesDto([
new AppLanguageDto('en', 'English', true, true, ['de', 'en']),
new AppLanguageDto('it', 'Italian', false, false, [])
]);
], new Version('2')));
}));
it('should make post request to add language',
@ -94,7 +131,7 @@ describe('AppLanguagesService', () => {
let language: AppLanguageDto | null = null;
appLanguagesService.postLanguages('my-app', dto, version).subscribe(result => {
language = result;
language = result.payload;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/languages');

59
src/Squidex/app/shared/services/app-languages.service.ts

@ -15,9 +15,30 @@ import {
AnalyticsService,
ApiUrlConfig,
HTTP,
Version
Version,
Versioned
} from 'framework';
export class AppLanguagesDto {
constructor(
public readonly languages: AppLanguageDto[],
public readonly version: Version
) {
}
public addLanguage(language: AppLanguageDto, version: Version) {
return new AppLanguagesDto([...this.languages, language], version);
}
public updateLanguage(language: AppLanguageDto, version: Version) {
return new AppLanguagesDto(this.languages.map(l => l.iso2Code === language.iso2Code ? language : l), version);
}
public removeLanguage(language: AppLanguageDto, version: Version) {
return new AppLanguagesDto(this.languages.filter(l => l.iso2Code !== language.iso2Code), version);
}
}
export class AppLanguageDto {
constructor(
public readonly iso2Code: string,
@ -58,14 +79,16 @@ export class AppLanguagesService {
) {
}
public getLanguages(appName: string, version?: Version): Observable<AppLanguageDto[]> {
public getLanguages(appName: string): Observable<AppLanguagesDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages`);
return HTTP.getVersioned(this.http, url, version)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
const languages = items.map(item => {
return new AppLanguageDto(
item.iso2Code,
item.englishName,
@ -73,21 +96,27 @@ export class AppLanguagesService {
item.isOptional === true,
item.fallback || []);
});
return new AppLanguagesDto(languages, response.version);
})
.pretifyError('Failed to load languages. Please reload.');
}
public postLanguages(appName: string, dto: AddAppLanguageDto, version: Version): Observable<AppLanguageDto> {
public postLanguages(appName: string, dto: AddAppLanguageDto, version: Version): Observable<Versioned<AppLanguageDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages`);
return HTTP.postVersioned(this.http, url, dto, version)
return HTTP.postVersioned<any>(this.http, url, dto, version)
.map(response => {
return new AppLanguageDto(
response.iso2Code,
response.englishName,
response.isMaster === true,
response.isOptional === true,
response.fallback || []);
const body = response.payload.body;
const language = new AppLanguageDto(
body.iso2Code,
body.englishName,
body.isMaster === true,
body.isOptional === true,
body.fallback || []);
return new Versioned(response.version, language);
})
.do(() => {
this.analytics.trackEvent('Language', 'Added', appName);
@ -95,7 +124,7 @@ export class AppLanguagesService {
.pretifyError('Failed to add language. Please reload.');
}
public updateLanguage(appName: string, languageCode: string, dto: UpdateAppLanguageDto, version: Version): Observable<any> {
public updateLanguage(appName: string, languageCode: string, dto: UpdateAppLanguageDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages/${languageCode}`);
return HTTP.putVersioned(this.http, url, dto, version)
@ -105,7 +134,7 @@ export class AppLanguagesService {
.pretifyError('Failed to change language. Please reload.');
}
public deleteLanguage(appName: string, languageCode: string, version: Version): Observable<any> {
public deleteLanguage(appName: string, languageCode: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages/${languageCode}`);
return HTTP.deleteVersioned(this.http, url, version)

12
src/Squidex/app/shared/services/apps.service.ts

@ -48,9 +48,11 @@ export class AppsService {
public getApps(): Observable<AppDto[]> {
const url = this.apiUrl.buildUrl('/api/apps');
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
return new AppDto(
@ -67,11 +69,13 @@ export class AppsService {
public postApp(dto: CreateAppDto, now?: DateTime): Observable<AppDto> {
const url = this.apiUrl.buildUrl('api/apps');
return HTTP.postVersioned(this.http, url, dto)
return HTTP.postVersioned<any>(this.http, url, dto)
.map(response => {
const body = response.payload.body;
now = now || DateTime.now();
return new AppDto(response.id, dto.name, 'Owner', now, now);
return new AppDto(body.id, dto.name, 'Owner', now, now);
})
.do(() => {
this.analytics.trackEvent('App', 'Created', dto.name);

44
src/Squidex/app/shared/services/assets.service.spec.ts

@ -18,7 +18,8 @@ import {
DateTime,
LocalCacheService,
UpdateAssetDto,
Version
Version,
Versioned
} from './../';
describe('AssetDto', () => {
@ -26,22 +27,24 @@ describe('AssetDto', () => {
const creator = 'not-me';
const modified = DateTime.now();
const modifier = 'me';
const version = new Version('2');
const version = new Version('1');
const newVersion = new Version('2');
it('should update name property and user info when renaming', () => {
const asset_1 = new AssetDto('1', creator, creator, creation, creation, 'name.png', 'png', 1, 1, 'image/png', false, 1, 1, version);
const asset_2 = asset_1.rename('new-name.png', modifier, modified);
const asset_2 = asset_1.rename('new-name.png', modifier, newVersion, modified);
expect(asset_2.fileName).toEqual('new-name.png');
expect(asset_2.lastModified).toEqual(modified);
expect(asset_2.lastModifiedBy).toEqual(modifier);
expect(asset_2.version).toEqual(newVersion);
});
it('should update file properties when uploading', () => {
const update = new AssetReplacedDto(2, 2, 'image/jpeg', true, 2, 2, new Version('2'));
const update = new AssetReplacedDto(2, 2, 'image/jpeg', true, 2, 2);
const asset_1 = new AssetDto('1', creator, creator, creation, creation, 'name.png', 'png', 1, 1, 'image/png', false, 1, 1, version);
const asset_2 = asset_1.update(update, modifier, modified);
const asset_2 = asset_1.update(update, modifier, newVersion, modified);
expect(asset_2.fileSize).toEqual(2);
expect(asset_2.fileVersion).toEqual(2);
@ -51,6 +54,7 @@ describe('AssetDto', () => {
expect(asset_2.pixelHeight).toEqual(2);
expect(asset_2.lastModified).toEqual(modified);
expect(asset_2.lastModifiedBy).toEqual(modifier);
expect(asset_2.version).toEqual(newVersion);
});
});
@ -164,7 +168,7 @@ describe('AssetsService', () => {
let asset: AssetDto | null = null;
assetsService.getAsset('my-app', '123', version).subscribe(result => {
assetsService.getAsset('my-app', '123').subscribe(result => {
asset = result;
});
@ -186,8 +190,11 @@ describe('AssetsService', () => {
mimeType: 'image/png',
isImage: true,
pixelWidth: 1024,
pixelHeight: 2048,
version: 11
pixelHeight: 2048
}, {
headers: {
etag: '2'
}
});
expect(asset).toEqual(
@ -203,7 +210,7 @@ describe('AssetsService', () => {
true,
1024,
2048,
new Version('11')));
new Version('2')));
}));
it('should provide entry from cache if not found',
@ -215,7 +222,7 @@ describe('AssetsService', () => {
let asset: AssetDto | null = null;
assetsService.getAsset('my-app', '123', version).subscribe(result => {
assetsService.getAsset('my-app', '123').subscribe(result => {
asset = result;
});
@ -291,8 +298,11 @@ describe('AssetsService', () => {
mimeType: 'image/png',
isImage: true,
pixelWidth: 1024,
pixelHeight: 2048,
version: 11
pixelHeight: 2048
}, {
headers: {
etag: '2'
}
});
expect(asset).toEqual(
@ -309,7 +319,7 @@ describe('AssetsService', () => {
true,
1024,
2048,
new Version('11')));
new Version('2')));
}));
it('should make put request to replace asset content',
@ -318,7 +328,7 @@ describe('AssetsService', () => {
let asset: AssetReplacedDto | null = null;
assetsService.replaceFile('my-app', '123', null!, version).subscribe(result => {
asset = <AssetReplacedDto>result;
asset = (<Versioned<AssetReplacedDto>>result).payload;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/assets/123/content');
@ -332,8 +342,7 @@ describe('AssetsService', () => {
mimeType: 'image/png',
isImage: true,
pixelWidth: 1024,
pixelHeight: 2048,
version: 11
pixelHeight: 2048
});
expect(asset).toEqual(
@ -342,8 +351,7 @@ describe('AssetsService', () => {
'image/png',
true,
1024,
2048,
new Version('11')));
2048));
}));
it('should make put request to update asset',

83
src/Squidex/app/shared/services/assets.service.ts

@ -15,7 +15,8 @@ import {
DateTime,
LocalCacheService,
HTTP,
Version
Version,
Versioned
} from 'framework';
export class AssetsDto {
@ -45,7 +46,7 @@ export class AssetDto {
) {
}
public update(update: AssetReplacedDto, user: string, now?: DateTime): AssetDto {
public update(update: AssetReplacedDto, user: string, version: Version, now?: DateTime): AssetDto {
return new AssetDto(
this.id,
this.createdBy, user,
@ -58,10 +59,10 @@ export class AssetDto {
update.isImage,
update.pixelWidth,
update.pixelHeight,
update.version);
version);
}
public rename(name: string, user: string, now?: DateTime): AssetDto {
public rename(name: string, user: string, version: Version, now?: DateTime): AssetDto {
return new AssetDto(
this.id,
this.createdBy, user,
@ -74,7 +75,7 @@ export class AssetDto {
this.isImage,
this.pixelWidth,
this.pixelHeight,
this.version);
version);
}
}
@ -92,8 +93,7 @@ export class AssetReplacedDto {
public readonly mimeType: string,
public readonly isImage: boolean,
public readonly pixelWidth: number | null,
public readonly pixelHeight: number | null,
public readonly version: Version
public readonly pixelHeight: number | null
) {
}
}
@ -130,11 +130,13 @@ export class AssetsService {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets?${fullQuery}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response.items;
const body = response.payload.body;
const items: any[] = body.items;
return new AssetsDto(response.total, items.map(item => {
return new AssetsDto(body.total, items.map(item => {
return new AssetDto(
item.id,
item.createdBy,
@ -162,7 +164,10 @@ export class AssetsService {
reportProgress: true
});
return this.http.request(req)
return this.http.request<any>(req)
.filter(event =>
event.type === HttpEventType.UploadProgress ||
event.type === HttpEventType.Response)
.map(event => {
if (event.type === HttpEventType.UploadProgress) {
const percentDone = event.total ? Math.round(100 * event.loaded / event.total) : 0;
@ -187,7 +192,7 @@ export class AssetsService {
response.isImage,
response.pixelWidth,
response.pixelHeight,
new Version(response.version.toString()));
new Version(event.headers.get('etag')));
this.localCache.set(`asset.${dto.id}`, dto, 5000);
@ -196,34 +201,32 @@ export class AssetsService {
})
.do(dto => {
this.analytics.trackEvent('Asset', 'Uploaded', appName);
if (dto instanceof AssetDto) {
this.localCache.set(`asset.${dto.id}`, dto, 5000);
}
})
.pretifyError('Failed to upload asset. Please reload.');
}
public getAsset(appName: string, id: string, version?: Version): Observable<AssetDto> {
public getAsset(appName: string, id: string): Observable<AssetDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const body = response.payload.body;
return new AssetDto(
response.id,
response.createdBy,
response.lastModifiedBy,
DateTime.parseISO_UTC(response.created),
DateTime.parseISO_UTC(response.lastModified),
response.fileName,
response.fileType,
response.fileSize,
response.fileVersion,
response.mimeType,
response.isImage,
response.pixelWidth,
response.pixelHeight,
new Version(response.version.toString()));
body.id,
body.createdBy,
body.lastModifiedBy,
DateTime.parseISO_UTC(body.created),
DateTime.parseISO_UTC(body.lastModified),
body.fileName,
body.fileType,
body.fileSize,
body.fileVersion,
body.mimeType,
body.isImage,
body.pixelWidth,
body.pixelHeight,
response.version);
})
.catch(error => {
if (error instanceof HttpErrorResponse && error.status === 404) {
@ -239,7 +242,7 @@ export class AssetsService {
.pretifyError('Failed to load assets. Please reload.');
}
public replaceFile(appName: string, id: string, file: File, version: Version): Observable<number | AssetReplacedDto> {
public replaceFile(appName: string, id: string, file: File, version: Version): Observable<number | Versioned<AssetReplacedDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}/content`);
const req = new HttpRequest('PUT', url, getFormData(file), {
@ -250,6 +253,9 @@ export class AssetsService {
});
return this.http.request(req)
.filter(event =>
event.type === HttpEventType.UploadProgress ||
event.type === HttpEventType.Response)
.map(event => {
if (event.type === HttpEventType.UploadProgress) {
const percentDone = event.total ? Math.round(100 * event.loaded / event.total) : 0;
@ -258,16 +264,15 @@ export class AssetsService {
} else if (event instanceof HttpResponse) {
const response: any = event.body;
const dto = new AssetReplacedDto(
const replaced = new AssetReplacedDto(
response.fileSize,
response.fileVersion,
response.mimeType,
response.isImage,
response.pixelWidth,
response.pixelHeight,
new Version(response.version.toString()));
response.pixelHeight);
return dto;
return new Versioned(new Version(event.headers.get('etag')), replaced);
}
})
.do(() => {
@ -276,7 +281,7 @@ export class AssetsService {
.pretifyError('Failed to replace asset. Please reload.');
}
public deleteAsset(appName: string, id: string, version: Version): Observable<any> {
public deleteAsset(appName: string, id: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}`);
return HTTP.deleteVersioned(this.http, url, version)
@ -288,7 +293,7 @@ export class AssetsService {
.pretifyError('Failed to delete asset. Please reload.');
}
public putAsset(appName: string, id: string, dto: UpdateAssetDto, version: Version): Observable<any> {
public putAsset(appName: string, id: string, dto: UpdateAssetDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}`);
return HTTP.putVersioned(this.http, url, dto, version)

47
src/Squidex/app/shared/services/contents.service.spec.ts

@ -25,50 +25,56 @@ describe('ContentDto', () => {
const modified = DateTime.now();
const modifier = 'me';
const version = new Version('1');
const newVersion = new Version('2');
it('should update data property and user info when updating', () => {
const content_1 = new ContentDto('1', 'Published', creator, creator, creation, creation, { data: 1 }, version);
const content_2 = content_1.update({ data: 2 }, modifier, modified);
const content_2 = content_1.update({ data: 2 }, modifier, newVersion, modified);
expect(content_2.data).toEqual({ data: 2 });
expect(content_2.lastModified).toEqual(modified);
expect(content_2.lastModifiedBy).toEqual(modifier);
expect(content_2.version).toEqual(newVersion);
});
it('should update status property and user info when publishing', () => {
const content_1 = new ContentDto('1', 'Draft', creator, creator, creation, creation, { data: 1 }, version);
const content_2 = content_1.publish(modifier, modified);
const content_2 = content_1.publish(modifier, newVersion, modified);
expect(content_2.status).toEqual('Published');
expect(content_2.lastModified).toEqual(modified);
expect(content_2.lastModifiedBy).toEqual(modifier);
expect(content_2.version).toEqual(newVersion);
});
it('should update status property and user info when unpublishing', () => {
const content_1 = new ContentDto('1', 'Published', creator, creator, creation, creation, { data: 1 }, version);
const content_2 = content_1.unpublish(modifier, modified);
const content_2 = content_1.unpublish(modifier, newVersion, modified);
expect(content_2.status).toEqual('Draft');
expect(content_2.lastModified).toEqual(modified);
expect(content_2.lastModifiedBy).toEqual(modifier);
expect(content_2.version).toEqual(newVersion);
});
it('should update status property and user info when archiving', () => {
const content_1 = new ContentDto('1', 'Draft', creator, creator, creation, creation, { data: 1 }, version);
const content_2 = content_1.archive(modifier, modified);
const content_2 = content_1.archive(modifier, newVersion, modified);
expect(content_2.status).toEqual('Archived');
expect(content_2.lastModified).toEqual(modified);
expect(content_2.lastModifiedBy).toEqual(modifier);
expect(content_2.version).toEqual(newVersion);
});
it('should update status property and user info when restoring', () => {
const content_1 = new ContentDto('1', 'Archived', creator, creator, creation, creation, { data: 1 }, version);
const content_2 = content_1.restore(modifier, modified);
const content_2 = content_1.restore(modifier, newVersion, modified);
expect(content_2.status).toEqual('Draft');
expect(content_2.lastModified).toEqual(modified);
expect(content_2.lastModifiedBy).toEqual(modifier);
expect(content_2.version).toEqual(newVersion);
});
it('should update data property when setting data', () => {
@ -79,13 +85,6 @@ describe('ContentDto', () => {
expect(content_2.data).toBe(newData);
});
it('should replace version', () => {
const content_1 = new ContentDto('1', 'Published', creator, creator, creation, creation, { data: 1 }, version);
const content_2 = content_1.replaceVersion(new Version('2'));
expect(content_2.version).toEqual(new Version('2'));
});
});
describe('ContentsService', () => {
@ -220,14 +219,14 @@ describe('ContentsService', () => {
let content: ContentDto | null = null;
contentsService.getContent('my-app', 'my-schema', '1', version).subscribe(result => {
contentsService.getContent('my-app', 'my-schema', '1').subscribe(result => {
content = result;
});
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema/1');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBe(version.value);
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({
id: 'id1',
@ -236,8 +235,11 @@ describe('ContentsService', () => {
createdBy: 'Created1',
lastModified: '2017-12-12T10:10',
lastModifiedBy: 'LastModifiedBy1',
version: 11,
data: {}
}, {
headers: {
etag: '2'
}
});
expect(content).toEqual(
@ -245,7 +247,7 @@ describe('ContentsService', () => {
DateTime.parseISO_UTC('2016-12-12T10:10'),
DateTime.parseISO_UTC('2017-12-12T10:10'),
{},
new Version('11')));
new Version('2')));
}));
it('should provide entry from cache if not found',
@ -257,14 +259,14 @@ describe('ContentsService', () => {
let content: ContentDto | null = null;
contentsService.getContent('my-app', 'my-schema', '1', version).subscribe(result => {
contentsService.getContent('my-app', 'my-schema', '1').subscribe(result => {
content = result;
});
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema/1');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBe(version.value);
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({}, { status: 404, statusText: '404' });
@ -294,8 +296,11 @@ describe('ContentsService', () => {
createdBy: 'Created1',
lastModified: '2017-12-12T10:10',
lastModifiedBy: 'LastModifiedBy1',
version: 11,
data: {}
}, {
headers: {
etag: '2'
}
});
expect(content).toEqual(
@ -303,7 +308,7 @@ describe('ContentsService', () => {
DateTime.parseISO_UTC('2016-12-12T10:10'),
DateTime.parseISO_UTC('2017-12-12T10:10'),
{},
new Version('11')));
new Version('2')));
}));
it('should make get request to get versioned content data',
@ -320,7 +325,7 @@ describe('ContentsService', () => {
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema/content1/1');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBe(version.value);
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush(response);

109
src/Squidex/app/shared/services/contents.service.ts

@ -17,7 +17,8 @@ import {
DateTime,
LocalCacheService,
HTTP,
Version
Version,
Versioned
} from 'framework';
export class ContentsDto {
@ -53,50 +54,40 @@ export class ContentDto {
this.version);
}
public publish(user: string, now?: DateTime): ContentDto {
return this.changeStatus('Published', user, now);
public publish(user: string, version: Version, now?: DateTime): ContentDto {
return this.changeStatus('Published', user, version, now);
}
public unpublish(user: string, now?: DateTime): ContentDto {
return this.changeStatus('Draft', user, now);
public unpublish(user: string, version: Version, now?: DateTime): ContentDto {
return this.changeStatus('Draft', user, version, now);
}
public archive(user: string, now?: DateTime): ContentDto {
return this.changeStatus('Archived', user, now);
public archive(user: string, version: Version, now?: DateTime): ContentDto {
return this.changeStatus('Archived', user, version, now);
}
public restore(user: string, now?: DateTime): ContentDto {
return this.changeStatus('Draft', user, now);
public restore(user: string, version: Version, now?: DateTime): ContentDto {
return this.changeStatus('Draft', user, version, now);
}
public replaceVersion(version: Version): ContentDto {
return new ContentDto(
this.id,
this.status,
this.createdBy, this.lastModifiedBy,
this.created, this.lastModified,
this.data,
version);
}
private changeStatus(status: string, user: string, now?: DateTime): ContentDto {
private changeStatus(status: string, user: string, version: Version, now?: DateTime): ContentDto {
return new ContentDto(
this.id,
status,
this.createdBy, user,
this.created, now || DateTime.now(),
this.data,
this.version);
version);
}
public update(data: any, user: string, now?: DateTime): ContentDto {
public update(data: any, user: string, version: Version, now?: DateTime): ContentDto {
return new ContentDto(
this.id,
this.status,
this.createdBy, user,
this.created, now || DateTime.now(),
data,
this.version);
version);
}
}
@ -143,11 +134,13 @@ export class ContentsService {
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}?${fullQuery}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response.items;
const body = response.payload.body;
const items: any[] = body.items;
return new ContentsDto(response.total, items.map(item => {
return new ContentsDto(body.total, items.map(item => {
return new ContentDto(
item.id,
item.status,
@ -162,20 +155,22 @@ export class ContentsService {
.pretifyError('Failed to load contents. Please reload.');
}
public getContent(appName: string, schemaName: string, id: string, version?: Version): Observable<ContentDto> {
public getContent(appName: string, schemaName: string, id: string): Observable<ContentDto> {
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}`);
return HTTP.getVersioned(this.http, url, version)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const body = response.payload.body;
return new ContentDto(
response.id,
response.status,
response.createdBy,
response.lastModifiedBy,
DateTime.parseISO_UTC(response.created),
DateTime.parseISO_UTC(response.lastModified),
response.data,
new Version(response.version.toString()));
body.id,
body.status,
body.createdBy,
body.lastModifiedBy,
DateTime.parseISO_UTC(body.created),
DateTime.parseISO_UTC(body.lastModified),
body.data,
response.version);
})
.catch(error => {
if (error instanceof HttpErrorResponse && error.status === 404) {
@ -194,24 +189,29 @@ export class ContentsService {
public getVersionData(appName: string, schemaName: string, id: string, version: Version): Observable<any> {
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/${version.value}`);
return HTTP.getVersioned(this.http, url, version)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
return response.payload.body;
})
.pretifyError('Failed to load data. Please reload.');
}
public postContent(appName: string, schemaName: string, dto: any, publish: boolean): Observable<ContentDto> {
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}?publish=${publish}`);
return HTTP.postVersioned(this.http, url, dto)
return HTTP.postVersioned<any>(this.http, url, dto)
.map(response => {
const body = response.payload.body;
return new ContentDto(
response.id,
response.status,
response.createdBy,
response.lastModifiedBy,
DateTime.parseISO_UTC(response.created),
DateTime.parseISO_UTC(response.lastModified),
response.data,
new Version(response.version.toString()));
body.id,
body.status,
body.createdBy,
body.lastModifiedBy,
DateTime.parseISO_UTC(body.created),
DateTime.parseISO_UTC(body.lastModified),
body.data,
response.version);
})
.do(content => {
this.analytics.trackEvent('Content', 'Created', appName);
@ -221,10 +221,15 @@ export class ContentsService {
.pretifyError('Failed to create content. Please reload.');
}
public putContent(appName: string, schemaName: string, id: string, dto: any, version: Version): Observable<any> {
public putContent(appName: string, schemaName: string, id: string, dto: any, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}`);
return HTTP.putVersioned(this.http, url, dto, version)
.map(response => {
const body = response.payload.body;
return new Versioned(response.version, body);
})
.do(() => {
this.analytics.trackEvent('Content', 'Updated', appName);
@ -233,7 +238,7 @@ export class ContentsService {
.pretifyError('Failed to update content. Please reload.');
}
public deleteContent(appName: string, schemaName: string, id: string, version: Version): Observable<any> {
public deleteContent(appName: string, schemaName: string, id: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}`);
return HTTP.deleteVersioned(this.http, url, version)
@ -245,7 +250,7 @@ export class ContentsService {
.pretifyError('Failed to delete content. Please reload.');
}
public publishContent(appName: string, schemaName: string, id: string, version: Version): Observable<any> {
public publishContent(appName: string, schemaName: string, id: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/publish`);
return HTTP.putVersioned(this.http, url, {}, version)
@ -255,7 +260,7 @@ export class ContentsService {
.pretifyError('Failed to publish content. Please reload.');
}
public unpublishContent(appName: string, schemaName: string, id: string, version: Version): Observable<any> {
public unpublishContent(appName: string, schemaName: string, id: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/unpublish`);
return HTTP.putVersioned(this.http, url, {}, version)
@ -265,7 +270,7 @@ export class ContentsService {
.pretifyError('Failed to unpublish content. Please reload.');
}
public archiveContent(appName: string, schemaName: string, id: string, version: Version): Observable<any> {
public archiveContent(appName: string, schemaName: string, id: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/archive`);
return HTTP.putVersioned(this.http, url, {}, version)
@ -275,7 +280,7 @@ export class ContentsService {
.pretifyError('Failed to archive content. Please reload.');
}
public restoreContent(appName: string, schemaName: string, id: string, version: Version): Observable<any> {
public restoreContent(appName: string, schemaName: string, id: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/restore`);
return HTTP.putVersioned(this.http, url, {}, version)

6
src/Squidex/app/shared/services/event-consumers.service.ts

@ -47,9 +47,11 @@ export class EventConsumersService {
public getEventConsumers(): Observable<EventConsumerDto[]> {
const url = this.apiUrl.buildUrl('/api/event-consumers');
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
return new EventConsumerDto(

6
src/Squidex/app/shared/services/history.service.ts

@ -39,9 +39,11 @@ export class HistoryService {
public getHistory(appName: string, channel: string): Observable<HistoryEventDto[]> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/history?channel=${channel}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
return new HistoryEventDto(

6
src/Squidex/app/shared/services/languages.service.ts

@ -32,9 +32,11 @@ export class LanguagesService {
public getLanguages(): Observable<LanguageDto[]> {
const url = this.apiUrl.buildUrl('api/languages');
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
return new LanguageDto(

13
src/Squidex/app/shared/services/plans.service.spec.ts

@ -44,14 +44,14 @@ describe('PlansService', () => {
let plans: AppPlansDto | null = null;
plansService.getPlans('my-app', version).subscribe(result => {
plansService.getPlans('my-app').subscribe(result => {
plans = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/plans');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBe(version.value);
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({
currentPlanId: '123',
@ -75,6 +75,10 @@ describe('PlansService', () => {
maxContributors: 6500
}
]
}, {
headers: {
etag: '2'
}
});
expect(plans).toEqual(
@ -85,7 +89,8 @@ describe('PlansService', () => {
[
new PlanDto('free', 'Free', '14 €', 1000, 1500, 2500),
new PlanDto('prof', 'Prof', '18 €', 4000, 5500, 6500)
]
],
new Version('2')
));
}));
@ -97,7 +102,7 @@ describe('PlansService', () => {
let planChanged: PlanChangedDto | null = null;
plansService.putPlan('my-app', dto, version).subscribe(result => {
planChanged = result;
planChanged = result.payload;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/plan');

40
src/Squidex/app/shared/services/plans.service.ts

@ -15,7 +15,8 @@ import {
AnalyticsService,
ApiUrlConfig,
HTTP,
Version
Version,
Versioned
} from 'framework';
export class AppPlansDto {
@ -23,9 +24,19 @@ export class AppPlansDto {
public readonly currentPlanId: string,
public readonly planOwner: string,
public readonly hasPortal: boolean,
public readonly plans: PlanDto[]
public readonly plans: PlanDto[],
public readonly version: Version
) {
}
public changePlanId(planId: string, version?: Version): AppPlansDto {
return new AppPlansDto(
planId,
this.planOwner,
this.hasPortal,
this.plans,
version || this.version);
}
}
export class PlanDto {
@ -63,17 +74,19 @@ export class PlansService {
) {
}
public getPlans(appName: string, version?: Version): Observable<AppPlansDto> {
public getPlans(appName: string): Observable<AppPlansDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/plans`);
return HTTP.getVersioned(this.http, url, version)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response.plans;
const body = response.payload.body;
const items: any[] = body.plans;
return new AppPlansDto(
response.currentPlanId,
response.planOwner,
response.hasPortal,
body.currentPlanId,
body.planOwner,
body.hasPortal,
items.map(item => {
return new PlanDto(
item.id,
@ -82,17 +95,20 @@ export class PlansService {
item.maxApiCalls,
item.maxAssetSize,
item.maxContributors);
}));
}),
response.version);
})
.pretifyError('Failed to load plans. Please reload.');
}
public putPlan(appName: string, dto: ChangePlanDto, version: Version): Observable<PlanChangedDto> {
public putPlan(appName: string, dto: ChangePlanDto, version: Version): Observable<Versioned<PlanChangedDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/plan`);
return HTTP.putVersioned(this.http, url, dto, version)
return HTTP.putVersioned<any>(this.http, url, dto, version)
.map(response => {
return new PlanChangedDto(response.redirectUri);
const body = response.payload.body;
return new Versioned(response.version, new PlanChangedDto(body.redirectUri));
})
.do(() => {
this.analytics.trackEvent('Plan', 'Changed', appName);

62
src/Squidex/app/shared/services/schemas.service.spec.ts

@ -34,34 +34,38 @@ describe('SchemaDto', () => {
const modified = DateTime.now();
const modifier = 'me';
const version = new Version('1');
const newVersion = new Version('2');
it('should update isPublished property and user info when publishing', () => {
const schema_1 = new SchemaDto('1', 'name', properties, false, creator, creator, creation, creation, version);
const schema_2 = schema_1.publish(modifier, modified);
const schema_2 = schema_1.publish(modifier, newVersion, modified);
expect(schema_2.isPublished).toBeTruthy();
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
it('should update isPublished property and user info when unpublishing', () => {
const schema_1 = new SchemaDto('1', 'name', properties, false, creator, creator, creation, creation, version);
const schema_2 = schema_1.unpublish(modifier, modified);
const schema_2 = schema_1.unpublish(modifier, newVersion, modified);
expect(schema_2.isPublished).toBeFalsy();
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
it('should update properties property and user info when updating', () => {
const newProperties = new SchemaPropertiesDto('New Name');
const schema_1 = new SchemaDto('1', 'name', properties, false, creator, creator, creation, creation, version);
const schema_2 = schema_1.update(newProperties, modifier, modified);
const schema_2 = schema_1.update(newProperties, modifier, newVersion, modified);
expect(schema_2.properties).toEqual(newProperties);
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
it('should update scripts properties and user info when configure scripts', () => {
@ -74,7 +78,7 @@ describe('SchemaDto', () => {
'<script-change>');
const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, creator, creator, creation, creation, version, []);
const schema_2 = schema_1.configureScripts(newScripts, modifier, modified);
const schema_2 = schema_1.configureScripts(newScripts, modifier, newVersion, modified);
expect(schema_2.scriptQuery).toEqual('<script-query>');
expect(schema_2.scriptCreate).toEqual('<script-create>');
@ -83,6 +87,7 @@ describe('SchemaDto', () => {
expect(schema_2.scriptChange).toEqual('<script-change>');
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
});
@ -93,34 +98,38 @@ describe('SchemaDetailsDto', () => {
const modified = DateTime.now();
const modifier = 'me';
const version = new Version('1');
const newVersion = new Version('2');
it('should update isPublished property and user info when publishing', () => {
const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, creator, creator, creation, creation, version, []);
const schema_2 = schema_1.publish(modifier, modified);
const schema_2 = schema_1.publish(modifier, newVersion, modified);
expect(schema_2.isPublished).toBeTruthy();
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
it('should update isPublished property and user info when unpublishing', () => {
const schema_1 = new SchemaDetailsDto('1', 'name', properties, true, creator, creator, creation, creation, version, []);
const schema_2 = schema_1.unpublish(modifier, modified);
const schema_2 = schema_1.unpublish(modifier, newVersion, modified);
expect(schema_2.isPublished).toBeFalsy();
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
it('should update properties property and user info when updating', () => {
const newProperties = new SchemaPropertiesDto('New Name');
const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, creator, creator, creation, creation, version, []);
const schema_2 = schema_1.update(newProperties, modifier, modified);
const schema_2 = schema_1.update(newProperties, modifier, newVersion, modified);
expect(schema_2.properties).toEqual(newProperties);
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
it('should update fields property and user info when adding field', () => {
@ -128,11 +137,12 @@ describe('SchemaDetailsDto', () => {
const field2 = new FieldDto(2, '2', false, false, false, 'l', createProperties('Number'));
const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, creator, creator, creation, creation, version, [field1]);
const schema_2 = schema_1.addField(field2, modifier, modified);
const schema_2 = schema_1.addField(field2, modifier, newVersion, modified);
expect(schema_2.fields).toEqual([field1, field2]);
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
it('should update fields property and user info when removing field', () => {
@ -140,11 +150,12 @@ describe('SchemaDetailsDto', () => {
const field2 = new FieldDto(2, '2', false, false, false, 'l', createProperties('Number'));
const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, creator, creator, creation, creation, version, [field1, field2]);
const schema_2 = schema_1.removeField(field1, modifier, modified);
const schema_2 = schema_1.removeField(field1, modifier, newVersion, modified);
expect(schema_2.fields).toEqual([field2]);
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
it('should update fields property and user info when replacing fields', () => {
@ -152,11 +163,12 @@ describe('SchemaDetailsDto', () => {
const field2 = new FieldDto(2, '2', false, false, false, 'l', createProperties('Number'));
const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, creator, creator, creation, creation, version, [field1, field2]);
const schema_2 = schema_1.replaceFields([field2, field1], modifier, modified);
const schema_2 = schema_1.replaceFields([field2, field1], modifier, newVersion, modified);
expect(schema_2.fields).toEqual([field2, field1]);
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
it('should update fields property and user info when updating field', () => {
@ -165,11 +177,12 @@ describe('SchemaDetailsDto', () => {
const field2_2 = new FieldDto(2, '2', false, false, false, 'l', createProperties('Boolean'));
const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, creator, creator, creation, creation, version, [field1_0, field2_1]);
const schema_2 = schema_1.updateField(field2_2, modifier, modified);
const schema_2 = schema_1.updateField(field2_2, modifier, newVersion, modified);
expect(schema_2.fields).toEqual([field1_0, field2_2]);
expect(schema_2.lastModified).toEqual(modified);
expect(schema_2.lastModifiedBy).toEqual(modifier);
expect(schema_2.version).toEqual(newVersion);
});
});
@ -264,7 +277,7 @@ describe('SchemasService', () => {
let schema: SchemaDetailsDto | null = null;
schemasService.getSchema('my-app', 'my-schema', version).subscribe(result => {
schemasService.getSchema('my-app', 'my-schema').subscribe(result => {
schema = result;
});
@ -281,7 +294,6 @@ describe('SchemasService', () => {
createdBy: 'Created1',
lastModified: '2017-12-12T10:10',
lastModifiedBy: 'LastModifiedBy1',
version: 11,
properties: {
label: 'label1',
hints: 'hints1'
@ -381,13 +393,17 @@ describe('SchemasService', () => {
scriptUpdate: '<script-update>',
scriptDelete: '<script-delete>',
scriptChange: '<script-change>'
}, {
headers: {
etag: '2'
}
});
expect(schema).toEqual(
new SchemaDetailsDto('id1', 'name1', new SchemaPropertiesDto('label1', 'hints1'), true, 'Created1', 'LastModifiedBy1',
DateTime.parseISO_UTC('2016-12-12T10:10'),
DateTime.parseISO_UTC('2017-12-12T10:10'),
new Version('11'),
new Version('2'),
[
new FieldDto(1, 'field1', true, true, true, 'language', createProperties('Number')),
new FieldDto(2, 'field2', true, true, true, 'language', createProperties('String')),
@ -414,7 +430,7 @@ describe('SchemasService', () => {
let schema: SchemaDetailsDto | null = null;
schemasService.getSchema('my-app', 'my-schema', version).subscribe(result => {
schemasService.getSchema('my-app', 'my-schema').subscribe(result => {
schema = result;
});
@ -435,19 +451,25 @@ describe('SchemasService', () => {
let schema: SchemaDetailsDto | null = null;
schemasService.postSchema('my-app', dto, user, now, version).subscribe(result => {
schemasService.postSchema('my-app', dto, user, now).subscribe(result => {
schema = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas');
expect(req.request.method).toEqual('POST');
expect(req.request.headers.get('If-Match')).toBe(version.value);
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({ id: '1' });
req.flush({
id: '1'
}, {
headers: {
etag: '2'
}
});
expect(schema).toEqual(
new SchemaDetailsDto('1', dto.name, new SchemaPropertiesDto(), false, user, user, now, now, version, []));
new SchemaDetailsDto('1', dto.name, new SchemaPropertiesDto(), false, user, user, now, now, new Version('2'), []));
}));
it('should make post request to add field',
@ -458,7 +480,7 @@ describe('SchemasService', () => {
let field: FieldDto | null = null;
schemasService.postField('my-app', 'my-schema', dto, version).subscribe(result => {
field = result;
field = result.payload;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields');

147
src/Squidex/app/shared/services/schemas.service.ts

@ -19,7 +19,8 @@ import {
LocalCacheService,
HTTP,
ValidatorsEx,
Version
Version,
Versioned
} from 'framework';
export const fieldTypes: string[] = [
@ -86,7 +87,7 @@ export class SchemaDto {
) {
}
public publish(user: string, now?: DateTime): SchemaDto {
public publish(user: string, version: Version, now?: DateTime): SchemaDto {
return new SchemaDto(
this.id,
this.name,
@ -94,10 +95,10 @@ export class SchemaDto {
true,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version);
version);
}
public unpublish(user: string, now?: DateTime): SchemaDto {
public unpublish(user: string, version: Version, now?: DateTime): SchemaDto {
return new SchemaDto(
this.id,
this.name,
@ -105,10 +106,10 @@ export class SchemaDto {
false,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version);
version);
}
public update(properties: SchemaPropertiesDto, user: string, now?: DateTime): SchemaDto {
public update(properties: SchemaPropertiesDto, user: string, version: Version, now?: DateTime): SchemaDto {
return new SchemaDto(
this.id,
this.name,
@ -116,7 +117,7 @@ export class SchemaDto {
this.isPublished,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version);
version);
}
}
@ -132,7 +133,7 @@ export class SchemaDetailsDto extends SchemaDto {
super(id, name, properties, isPublished, createdBy, lastModifiedBy, created, lastModified, version);
}
public publish(user: string, now?: DateTime): SchemaDetailsDto {
public publish(user: string, version: Version, now?: DateTime): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
@ -140,7 +141,7 @@ export class SchemaDetailsDto extends SchemaDto {
true,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version,
version,
this.fields,
this.scriptQuery,
this.scriptCreate,
@ -149,7 +150,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.scriptChange);
}
public unpublish(user: string, now?: DateTime): SchemaDetailsDto {
public unpublish(user: string, version: Version, now?: DateTime): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
@ -157,7 +158,7 @@ export class SchemaDetailsDto extends SchemaDto {
false,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version,
version,
this.fields,
this.scriptQuery,
this.scriptCreate,
@ -166,7 +167,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.scriptChange);
}
public configureScripts(scripts: UpdateSchemaScriptsDto, user: string, now?: DateTime): SchemaDetailsDto {
public configureScripts(scripts: UpdateSchemaScriptsDto, user: string, version: Version, now?: DateTime): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
@ -174,7 +175,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.isPublished,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version,
version,
this.fields,
scripts.scriptQuery,
scripts.scriptCreate,
@ -183,7 +184,7 @@ export class SchemaDetailsDto extends SchemaDto {
scripts.scriptChange);
}
public update(properties: SchemaPropertiesDto, user: string, now?: DateTime): SchemaDetailsDto {
public update(properties: SchemaPropertiesDto, user: string, version: Version, now?: DateTime): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
@ -191,7 +192,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.isPublished,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version,
version,
this.fields,
this.scriptQuery,
this.scriptCreate,
@ -200,7 +201,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.scriptChange);
}
public addField(field: FieldDto, user: string, now?: DateTime): SchemaDetailsDto {
public addField(field: FieldDto, user: string, version: Version, now?: DateTime): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
@ -208,7 +209,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.isPublished,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version,
version,
[...this.fields, field],
this.scriptQuery,
this.scriptCreate,
@ -217,7 +218,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.scriptChange);
}
public updateField(field: FieldDto, user: string, now?: DateTime): SchemaDetailsDto {
public updateField(field: FieldDto, user: string, version: Version, now?: DateTime): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
@ -225,7 +226,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.isPublished,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version,
version,
this.fields.map(f => f.fieldId === field.fieldId ? field : f),
this.scriptQuery,
this.scriptCreate,
@ -234,7 +235,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.scriptChange);
}
public replaceFields(fields: FieldDto[], user: string, now?: DateTime): SchemaDetailsDto {
public replaceFields(fields: FieldDto[], user: string, version: Version, now?: DateTime): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
@ -242,7 +243,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.isPublished,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version,
version,
fields,
this.scriptQuery,
this.scriptCreate,
@ -251,7 +252,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.scriptChange);
}
public removeField(field: FieldDto, user: string, now?: DateTime): SchemaDetailsDto {
public removeField(field: FieldDto, user: string, version: Version, now?: DateTime): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
@ -259,7 +260,7 @@ export class SchemaDetailsDto extends SchemaDto {
this.isPublished,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version,
version,
this.fields.filter(f => f.fieldId !== field.fieldId),
this.scriptQuery,
this.scriptCreate,
@ -698,9 +699,11 @@ export class SchemasService {
public getSchemas(appName: string): Observable<SchemaDto[]> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
const properties = new SchemaPropertiesDto(item.properties.label, item.properties.hints);
@ -719,12 +722,14 @@ export class SchemasService {
.pretifyError('Failed to load schemas. Please reload.');
}
public getSchema(appName: string, id: string, version?: Version): Observable<SchemaDetailsDto> {
public getSchema(appName: string, id: string): Observable<SchemaDetailsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${id}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const fields = response.fields.map((item: any) => {
const body = response.payload.body;
const fields = body.fields.map((item: any) => {
const propertiesDto =
createProperties(
item.properties.fieldType,
@ -740,23 +745,23 @@ export class SchemasService {
propertiesDto);
});
const properties = new SchemaPropertiesDto(response.properties.label, response.properties.hints);
const properties = new SchemaPropertiesDto(body.properties.label, body.properties.hints);
return new SchemaDetailsDto(
response.id,
response.name, properties,
response.isPublished,
response.createdBy,
response.lastModifiedBy,
DateTime.parseISO_UTC(response.created),
DateTime.parseISO_UTC(response.lastModified),
new Version(response.version.toString()),
body.id,
body.name, properties,
body.isPublished,
body.createdBy,
body.lastModifiedBy,
DateTime.parseISO_UTC(body.created),
DateTime.parseISO_UTC(body.lastModified),
response.version,
fields,
response.scriptQuery,
response.scriptCreate,
response.scriptUpdate,
response.scriptDelete,
response.scriptChange);
body.scriptQuery,
body.scriptCreate,
body.scriptUpdate,
body.scriptDelete,
body.scriptChange);
})
.catch(error => {
if (error instanceof HttpErrorResponse && error.status === 404) {
@ -772,15 +777,17 @@ export class SchemasService {
.pretifyError('Failed to load schema. Please reload.');
}
public postSchema(appName: string, dto: CreateSchemaDto, user: string, now: DateTime, version: Version): Observable<SchemaDetailsDto> {
public postSchema(appName: string, dto: CreateSchemaDto, user: string, now: DateTime): Observable<SchemaDetailsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`);
return HTTP.postVersioned(this.http, url, dto, version)
return HTTP.postVersioned<any>(this.http, url, dto)
.map(response => {
const body = response.payload.body;
now = now || DateTime.now();
return new SchemaDetailsDto(
response.id,
body.id,
dto.name,
dto.properties || new SchemaPropertiesDto(),
false,
@ -788,13 +795,13 @@ export class SchemasService {
user,
now,
now,
version,
response.version,
dto.fields || [],
response.scriptQuery,
response.scriptCreate,
response.scriptUpdate,
response.scriptDelete,
response.scriptChange);
body.scriptQuery,
body.scriptCreate,
body.scriptUpdate,
body.scriptDelete,
body.scriptChange);
})
.do(schema => {
this.analytics.trackEvent('Schema', 'Created', appName);
@ -805,19 +812,23 @@ export class SchemasService {
.pretifyError('Failed to create schema. Please reload.');
}
public postField(appName: string, schemaName: string, dto: AddFieldDto, version: Version): Observable<FieldDto> {
public postField(appName: string, schemaName: string, dto: AddFieldDto, version: Version): Observable<Versioned<FieldDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields`);
return HTTP.postVersioned(this.http, url, dto, version)
return HTTP.postVersioned<any>(this.http, url, dto, version)
.map(response => {
return new FieldDto(
response.id,
const body = response.payload.body;
const field = new FieldDto(
body.id,
dto.name,
false,
false,
false,
dto.partitioning,
dto.properties);
return new Versioned(response.version, field);
})
.do(() => {
this.analytics.trackEvent('Schema', 'FieldCreated', appName);
@ -825,7 +836,7 @@ export class SchemasService {
.pretifyError('Failed to add field. Please reload.');
}
public deleteSchema(appName: string, schemaName: string, version: Version): Observable<any> {
public deleteSchema(appName: string, schemaName: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`);
return HTTP.deleteVersioned(this.http, url, version)
@ -838,7 +849,7 @@ export class SchemasService {
.pretifyError('Failed to delete schema. Please reload.');
}
public putSchemaScripts(appName: string, schemaName: string, dto: UpdateSchemaScriptsDto, version: Version): Observable<any> {
public putSchemaScripts(appName: string, schemaName: string, dto: UpdateSchemaScriptsDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/scripts`);
return HTTP.putVersioned(this.http, url, dto, version)
@ -848,7 +859,7 @@ export class SchemasService {
.pretifyError('Failed to update schema scripts. Please reload.');
}
public putSchema(appName: string, schemaName: string, dto: UpdateSchemaDto, version: Version): Observable<any> {
public putSchema(appName: string, schemaName: string, dto: UpdateSchemaDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`);
return HTTP.putVersioned(this.http, url, dto, version)
@ -858,7 +869,7 @@ export class SchemasService {
.pretifyError('Failed to update schema. Please reload.');
}
public putFieldOrdering(appName: string, schemaName: string, dto: number[], version: Version): Observable<any> {
public putFieldOrdering(appName: string, schemaName: string, dto: number[], version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/ordering`);
return HTTP.putVersioned(this.http, url, { fieldIds: dto }, version)
@ -868,7 +879,7 @@ export class SchemasService {
.pretifyError('Failed to reorder fields. Please reload.');
}
public publishSchema(appName: string, schemaName: string, version: Version): Observable<any> {
public publishSchema(appName: string, schemaName: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/publish`);
return HTTP.putVersioned(this.http, url, {}, version)
@ -878,7 +889,7 @@ export class SchemasService {
.pretifyError('Failed to publish schema. Please reload.');
}
public unpublishSchema(appName: string, schemaName: string, version: Version): Observable<any> {
public unpublishSchema(appName: string, schemaName: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/unpublish`);
return HTTP.putVersioned(this.http, url, {}, version)
@ -888,7 +899,7 @@ export class SchemasService {
.pretifyError('Failed to unpublish schema. Please reload.');
}
public putField(appName: string, schemaName: string, fieldId: number, dto: UpdateFieldDto, version: Version): Observable<any> {
public putField(appName: string, schemaName: string, fieldId: number, dto: UpdateFieldDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}`);
return HTTP.putVersioned(this.http, url, dto, version)
@ -898,7 +909,7 @@ export class SchemasService {
.pretifyError('Failed to update field. Please reload.');
}
public enableField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<any> {
public enableField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/enable`);
return HTTP.putVersioned(this.http, url, {}, version)
@ -908,7 +919,7 @@ export class SchemasService {
.pretifyError('Failed to enable field. Please reload.');
}
public disableField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<any> {
public disableField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/disable`);
return HTTP.putVersioned(this.http, url, {}, version)
@ -918,7 +929,7 @@ export class SchemasService {
.pretifyError('Failed to disable field. Please reload.');
}
public lockField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<any> {
public lockField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/lock`);
return HTTP.putVersioned(this.http, url, {}, version)
@ -928,7 +939,7 @@ export class SchemasService {
.pretifyError('Failed to lock field. Please reload.');
}
public showField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<any> {
public showField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/show`);
return HTTP.putVersioned(this.http, url, {}, version)
@ -938,7 +949,7 @@ export class SchemasService {
.pretifyError('Failed to show field. Please reload.');
}
public hideField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<any> {
public hideField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/hide`);
return HTTP.putVersioned(this.http, url, {}, version)
@ -948,7 +959,7 @@ export class SchemasService {
.pretifyError('Failed to hide field. Please reload.');
}
public deleteField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<any> {
public deleteField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}`);
return HTTP.deleteVersioned(this.http, url, version)

24
src/Squidex/app/shared/services/usages.service.ts

@ -62,9 +62,11 @@ export class UsagesService {
public getMonthCalls(app: string): Observable<CurrentCallsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${app}/usages/calls/month`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
return new CurrentCallsDto(response.count, response.maxAllowed);
const body = response.payload.body;
return new CurrentCallsDto(body.count, body.maxAllowed);
})
.pretifyError('Failed to load monthly api calls. Please reload.');
}
@ -72,9 +74,11 @@ export class UsagesService {
public getTodayStorage(app: string): Observable<CurrentStorageDto> {
const url = this.apiUrl.buildUrl(`api/apps/${app}/usages/storage/today`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
return new CurrentStorageDto(response.size, response.maxAllowed);
const body = response.payload.body;
return new CurrentStorageDto(body.size, body.maxAllowed);
})
.pretifyError('Failed to load todays storage size. Please reload.');
}
@ -82,9 +86,11 @@ export class UsagesService {
public getCallsUsages(app: string, fromDate: DateTime, toDate: DateTime): Observable<CallsUsageDto[]> {
const url = this.apiUrl.buildUrl(`api/apps/${app}/usages/calls/${fromDate.toUTCStringFormat('YYYY-MM-DD')}/${toDate.toUTCStringFormat('YYYY-MM-DD')}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
return new CallsUsageDto(
@ -99,9 +105,11 @@ export class UsagesService {
public getStorageUsages(app: string, fromDate: DateTime, toDate: DateTime): Observable<StorageUsageDto[]> {
const url = this.apiUrl.buildUrl(`api/apps/${app}/usages/storage/${fromDate.toUTCStringFormat('YYYY-MM-DD')}/${toDate.toUTCStringFormat('YYYY-MM-DD')}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
return new StorageUsageDto(

48
src/Squidex/app/shared/services/users.service.ts

@ -73,9 +73,11 @@ export class UsersService {
public getUsers(query?: string): Observable<UserDto[]> {
const url = this.apiUrl.buildUrl(`api/users?query=${query || ''}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const body = response.payload.body;
const items: any[] = body;
return items.map(item => {
return new UserDto(
@ -92,14 +94,16 @@ export class UsersService {
public getUser(id: string): Observable<UserDto> {
const url = this.apiUrl.buildUrl(`api/users/${id}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const body = response.payload.body;
return new UserDto(
response.id,
response.email,
response.displayName,
response.pictureUrl,
response.isLocked);
body.id,
body.email,
body.displayName,
body.pictureUrl,
body.isLocked);
})
.pretifyError('Failed to load user. Please reload.');
}
@ -116,9 +120,11 @@ export class UserManagementService {
public getUsers(take: number, skip: number, query?: string): Observable<UsersDto> {
const url = this.apiUrl.buildUrl(`api/user-management?take=${take}&skip=${skip}&query=${query || ''}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response.items;
const body = response.payload.body;
const items: any[] = body.items;
const users = items.map(item => {
return new UserDto(
@ -129,7 +135,7 @@ export class UserManagementService {
item.isLocked);
});
return new UsersDto(response.total, users);
return new UsersDto(body.total, users);
})
.pretifyError('Failed to load users. Please reload.');
}
@ -137,14 +143,16 @@ export class UserManagementService {
public getUser(id: string): Observable<UserDto> {
const url = this.apiUrl.buildUrl(`api/user-management/${id}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const body = response.payload.body;
return new UserDto(
response.id,
response.email,
response.displayName,
response.pictureUrl,
response.isLocked);
body.id,
body.email,
body.displayName,
body.pictureUrl,
body.isLocked);
})
.pretifyError('Failed to load user. Please reload.');
}
@ -152,9 +160,11 @@ export class UserManagementService {
public postUser(dto: CreateUserDto): Observable<UserDto> {
const url = this.apiUrl.buildUrl('api/user-management');
return HTTP.postVersioned(this.http, url, dto)
return HTTP.postVersioned<any>(this.http, url, dto)
.map(response => {
return new UserDto(response.id, dto.email, dto.displayName, response.pictureUrl, false);
const body = response.payload.body;
return new UserDto(body.id, dto.email, dto.displayName, body.pictureUrl, false);
})
.pretifyError('Failed to create user. Please reload.');
}

14
src/Squidex/app/shared/services/webhooks.service.spec.ts

@ -29,6 +29,7 @@ describe('WebhookDto', () => {
const modified = DateTime.now();
const modifier = 'me';
const version = new Version('1');
const newVersion = new Version('2');
it('should update url and schemas', () => {
const webhook_1 = new WebhookDto('id1', 'token1', creator, creator, creation, creation, version, [], 'http://squidex.io/hook', 1, 2, 3, 4);
@ -37,12 +38,13 @@ describe('WebhookDto', () => {
[
new WebhookSchemaDto('1', true, true, true, true),
new WebhookSchemaDto('2', true, true, true, true)
]), modifier, modified);
]), modifier, newVersion, modified);
expect(webhook_2.url).toEqual('http://squidex.io/hook2');
expect(webhook_2.schemas.length).toEqual(2);
expect(webhook_2.lastModified).toEqual(modified);
expect(webhook_2.lastModifiedBy).toEqual(modifier);
expect(webhook_2.version).toEqual(newVersion);
});
});
@ -132,16 +134,20 @@ describe('WebhooksService', () => {
let webhook: WebhookDto | null = null;
webhooksService.postWebhook('my-app', dto, user, now, version).subscribe(result => {
webhooksService.postWebhook('my-app', dto, user, now).subscribe(result => {
webhook = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/webhooks');
expect(req.request.method).toEqual('POST');
expect(req.request.headers.get('If-Match')).toEqual(version.value);
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({ id: 'id1', sharedSecret: 'token1', schemaId: 'schema1' });
req.flush({ id: 'id1', sharedSecret: 'token1', schemaId: 'schema1' }, {
headers: {
etag: '1'
}
});
expect(webhook).toEqual(
new WebhookDto('id1', 'token1', user, user, now, now, version, [], 'http://squidex.io/hook', 0, 0, 0, 0));

33
src/Squidex/app/shared/services/webhooks.service.ts

@ -16,7 +16,8 @@ import {
ApiUrlConfig,
DateTime,
HTTP,
Version
Version,
Versioned
} from 'framework';
export class WebhookDto {
@ -37,13 +38,13 @@ export class WebhookDto {
) {
}
public update(update: UpdateWebhookDto, user: string, now?: DateTime): WebhookDto {
public update(update: UpdateWebhookDto, user: string, version: Version, now?: DateTime): WebhookDto {
return new WebhookDto(
this.id,
this.sharedSecret,
this.createdBy, user,
this.created, now || DateTime.now(),
this.version,
version,
update.schemas,
update.url,
this.totalSucceeded,
@ -115,9 +116,9 @@ export class WebhooksService {
public getWebhooks(appName: string): Observable<WebhookDto[]> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/webhooks`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response;
const items: any[] = response.payload.body;
return items.map(item => {
const schemas = item.schemas.map((schema: any) =>
@ -147,19 +148,21 @@ export class WebhooksService {
.pretifyError('Failed to load webhooks. Please reload.');
}
public postWebhook(appName: string, dto: CreateWebhookDto, user: string, now: DateTime, version: Version): Observable<WebhookDto> {
public postWebhook(appName: string, dto: CreateWebhookDto, user: string, now: DateTime): Observable<WebhookDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/webhooks`);
return HTTP.postVersioned(this.http, url, dto, version)
return HTTP.postVersioned<any>(this.http, url, dto)
.map(response => {
const body = response.payload.body;
return new WebhookDto(
response.id,
response.sharedSecret,
body.id,
body.sharedSecret,
user,
user,
now,
now,
version,
response.version,
dto.schemas,
dto.url,
0, 0, 0, 0);
@ -170,7 +173,7 @@ export class WebhooksService {
.pretifyError('Failed to create webhook. Please reload.');
}
public putWebhook(appName: string, id: string, dto: UpdateWebhookDto, version: Version): Observable<any> {
public putWebhook(appName: string, id: string, dto: UpdateWebhookDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/webhooks/${id}`);
return HTTP.putVersioned(this.http, url, dto, version)
@ -193,11 +196,13 @@ export class WebhooksService {
public getEvents(appName: string, take: number, skip: number): Observable<WebhookEventsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/webhooks/events?take=${take}&skip=${skip}`);
return HTTP.getVersioned(this.http, url)
return HTTP.getVersioned<any>(this.http, url)
.map(response => {
const items: any[] = response.items;
const body = response.payload.body;
const items: any[] = body.items;
return new WebhookEventsDto(response.total, items.map(item => {
return new WebhookEventsDto(body.total, items.map(item => {
return new WebhookEventDto(
item.id,
DateTime.parseISO_UTC(item.created),

Loading…
Cancel
Save