Browse Source

Removing the US Specific fields and mostly just making this a map change. Users can also use the API and submit an address without lat/long that Google will try to geocode.

pull/204/head
Alex Van Dyke 8 years ago
parent
commit
390ce3ecd1
  1. 19
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs
  2. 5
      src/Squidex.Infrastructure/Geocoding/GoogleMapsGeocoder.cs
  3. 5
      src/Squidex.Infrastructure/Geocoding/IGeocoder.cs
  4. 5
      src/Squidex.Infrastructure/Geocoding/OSMGeocoder.cs
  5. 4
      src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs
  6. 5
      src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs
  7. 5
      src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs
  8. 6
      src/Squidex/Areas/Api/Controllers/UI/Models/UISettingsDto.cs
  9. 8
      src/Squidex/Areas/Api/Controllers/UI/UIController.cs
  10. 14
      src/Squidex/app/shared/components/app-context.ts
  11. 28
      src/Squidex/app/shared/components/geolocation-editor.component.html
  12. 20
      src/Squidex/app/shared/components/geolocation-editor.component.scss
  13. 102
      src/Squidex/app/shared/components/geolocation-editor.component.ts
  14. 50
      src/Squidex/app/shared/services/apps-store.service.spec.ts
  15. 18
      src/Squidex/app/shared/services/apps-store.service.ts
  16. 15
      src/Squidex/app/shared/services/apps.service.spec.ts
  17. 8
      src/Squidex/app/shared/services/apps.service.ts
  18. 2
      src/Squidex/app/shared/services/ui.service.spec.ts
  19. 3
      src/Squidex/app/shared/services/ui.service.ts
  20. 32
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/GeolocationFieldTests.cs
  21. 2
      tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandMiddlewareTests.cs

19
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs

@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
List<string> addressString = new List<string>();
var validProperties = new string[]
{
"latitude", "longitude", "address1", "address2", "city", "state", "zip"
"latitude", "longitude", "address"
};
foreach (var property in geolocation.Properties())
@ -78,19 +78,16 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
{
throw new InvalidCastException("Geolocation must have proper properties.");
}
addressString.Add(geolocation[property.Name.ToLower()]?.ToString());
}
var lat = geolocation["latitude"];
var lon = geolocation["longitude"];
var state = geolocation["state"]?.ToString();
var zip = geolocation["zip"]?.ToString();
var address = geolocation["address"]?.ToString();
if (lat == null || lon == null ||
((JValue)lat).Value == null || ((JValue)lon).Value == null)
{
var response = geocoder.GeocodeAddress(string.Join(string.Empty, addressString));
var response = geocoder.GeocodeAddress(address);
lat = response.TryGetPropertyValue("Latitude", (JToken)null);
lon = response.TryGetPropertyValue("Longitude", (JToken)null);
@ -108,16 +105,6 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
throw new InvalidCastException("Longitude must be between -180 and 180.");
}
if (!string.IsNullOrWhiteSpace(state) && !Regex.IsMatch(state, "[A-Z]{2}"))
{
throw new InvalidCastException("State must be two capital letters.");
}
if (!string.IsNullOrWhiteSpace(zip) && !Regex.IsMatch(zip, "[0-9]{5}(\\-[0-9]{4})?"))
{
throw new InvalidCastException("ZIP Code must match postal code with optional suffix pattern.");
}
return geolocation;
}

5
src/Squidex.Infrastructure/Geocoding/GoogleMapsGeocoder.cs

@ -1,6 +1,9 @@
// ==========================================================================
// GoogleMapsGeocoder.cs
// CivicPlus implementation of Squidex Headless CMS
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;

5
src/Squidex.Infrastructure/Geocoding/IGeocoder.cs

@ -1,6 +1,9 @@
// ==========================================================================
// IGeocoder.cs
// CivicPlus implementation of Squidex Headless CMS
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Infrastructure.Geocoding

5
src/Squidex.Infrastructure/Geocoding/OSMGeocoder.cs

@ -1,6 +1,9 @@
// ==========================================================================
// OSMGeocoder.cs
// CivicPlus implementation of Squidex Headless CMS
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;

4
src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs

@ -69,7 +69,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
var response = apps.Select(a =>
{
var dto = SimpleMapper.Map(a, new AppDto() { GeocoderKey = geocoder.Key ?? string.Empty });
var dto = SimpleMapper.Map(a, new AppDto());
dto.Permission = a.Contributors[subject];
@ -108,7 +108,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
var context = await CommandBus.PublishAsync(command);
var result = context.Result<EntityCreatedResult<Guid>>();
var response = new AppCreatedDto { Id = result.IdOrValue.ToString(), Version = result.Version, GeocoderKey = geocoder.Key ?? string.Empty };
var response = new AppCreatedDto { Id = result.IdOrValue.ToString(), Version = result.Version };
response.Permission = AppContributorPermission.Owner;

5
src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs

@ -41,10 +41,5 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// Gets the next plan name.
/// </summary>
public string PlanUpgrade { get; set; }
/// <summary>
/// The geocoding api key for the application
/// </summary>
public string GeocoderKey { get; set; }
}
}

5
src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs

@ -59,10 +59,5 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// Gets the next plan name.
/// </summary>
public string PlanUpgrade { get; set; }
/// <summary>
/// The geocoding api key for the application
/// </summary>
public string GeocoderKey { get; set; }
}
}

6
src/Squidex/Areas/Api/Controllers/UI/Models/UISettingsDto.cs

@ -18,5 +18,11 @@ namespace Squidex.Areas.Api.Controllers.UI.Models
/// </summary>
[Required]
public List<UIRegexSuggestionDto> RegexSuggestions { get; set; }
/// <summary>
/// The geocoder key.
/// </summary>
[Required]
public string GeocoderKey { get; set; }
}
}

8
src/Squidex/Areas/Api/Controllers/UI/UIController.cs

@ -14,6 +14,7 @@ using NSwag.Annotations;
using Squidex.Areas.Api.Controllers.UI.Models;
using Squidex.Config;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Geocoding;
using Squidex.Pipeline;
namespace Squidex.Areas.Api.Controllers.UI
@ -26,11 +27,13 @@ namespace Squidex.Areas.Api.Controllers.UI
public sealed class UIController : ApiController
{
private readonly MyUIOptions uiOptions;
private readonly IGeocoder geocoder;
public UIController(ICommandBus commandBus, IOptions<MyUIOptions> uiOptions)
public UIController(ICommandBus commandBus, IOptions<MyUIOptions> uiOptions, IGeocoder geocoder)
: base(commandBus)
{
this.uiOptions = uiOptions.Value;
this.geocoder = geocoder;
}
/// <summary>
@ -50,7 +53,8 @@ namespace Squidex.Areas.Api.Controllers.UI
!string.IsNullOrWhiteSpace(x.Key) &&
!string.IsNullOrWhiteSpace(x.Value))
.Select(x => new UIRegexSuggestionDto { Name = x.Key, Pattern = x.Value }).ToList()
?? new List<UIRegexSuggestionDto>()
?? new List<UIRegexSuggestionDto>(),
GeocoderKey = geocoder.Key ?? string.Empty
};
return Ok(dto);

14
src/Squidex/app/shared/components/app-context.ts

@ -18,13 +18,16 @@ import {
DialogService,
ErrorDto,
Notification,
Profile
Profile,
UISettingsDto
} from './../declarations-base';
@Injectable()
export class AppContext implements OnDestroy {
private readonly appSubscription: Subscription;
private readonly uiSettingsSubscription: Subscription;
private appField: AppDto;
private uiSettingsField: UISettingsDto;
public get app(): AppDto {
return this.appField;
@ -38,6 +41,10 @@ export class AppContext implements OnDestroy {
return this.appField ? this.appField.name : '';
}
public get uiSettings() {
return this.uiSettingsField;
}
public get userToken(): string {
return this.authService.user!.token;
}
@ -61,6 +68,11 @@ export class AppContext implements OnDestroy {
this.appsStore.selectedApp.take(1).subscribe(app => {
this.appField = app;
});
this.uiSettingsSubscription =
this.appsStore.uiSettings.subscribe(settings => {
this.uiSettingsField = settings;
});
}
public ngOnDestroy() {

28
src/Squidex/app/shared/components/geolocation-editor.component.html

@ -4,39 +4,17 @@
<input id="pac-input" class="form-control controls" type="text" placeholder="Search Google Maps" #searchBox />
</form>
<div>
<form [formGroup]="geolocationForm" (change)="updateValueByInput()" [class.form-inline]="!useGoogleMaps">
<sqx-control-errors for="state"></sqx-control-errors>
<sqx-control-errors for="zip"></sqx-control-errors>
<div class="form-group latitude-group" [hidden]="useGoogleMaps">
<form class="form-inline" [formGroup]="geolocationForm" (change)="updateValueByInput()" (submit)="updateValueByInput()">
<div class="form-group latitude-group">
<sqx-control-errors for="latitude" style="z-index: 10000;"></sqx-control-errors>
<label for="latitude">Lat. </label>
<input type="number" class="form-control" formControlName="latitude" step="any" />
</div>
<div class="form-group longitude-group" [hidden]="useGoogleMaps">
<div class="form-group longitude-group">
<sqx-control-errors for="longitude" style="z-index: 10000;"></sqx-control-errors>
<label for="longitude">Long. </label>
<input type="number" class="form-control" formControlName="longitude" step="any" />
</div>
<div class="form-group address1-group" [hidden]="!useGoogleMaps">
<label for="latitude">Address 1 </label>
<input type="text" class="form-control" formControlName="address1"/>
</div>
<div class="form-group address2-group" [hidden]="!useGoogleMaps">
<label for="longitude">Address 2 </label>
<input type="text" class="form-control" formControlName="address2"/>
</div>
<div class="form-group city-group" [hidden]="!useGoogleMaps" [class.hasClear]="hasValue">
<label for="latitude">City </label>
<input type="text" class="form-control" formControlName="city"/>
</div>
<div class="form-group state-group" [hidden]="!useGoogleMaps">
<label for="longitude">State </label>
<input type="text" class="form-control" formControlName="state" sqxUpperCaseInput/>
</div>
<div class="form-group zip-group" [hidden]="!useGoogleMaps">
<label for="longitude">ZIP Code </label>
<input type="text" class="form-control" formControlName="zip"/>
</div>
<div class="form-group clear-group" [class.hidden]="!hasValue">
<button type="reset" class="btn btn-link clear" [disabled]="isDisabled" (click)="reset()">Clear</button>
</div>

20
src/Squidex/app/shared/components/geolocation-editor.component.scss

@ -9,26 +9,6 @@
margin-top: .5rem;
margin-bottom: .5rem;
&.city-group {
display: inline-block;
width: 70%;
&.hasClear{
width: 55%;
}
}
&.state-group {
display: inline-block;
width: 10%;
}
&.zip-group {
display: inline-block;
width: 18.5%;
}
&.clear-group {
display: inline-block;
&.hidden{

102
src/Squidex/app/shared/components/geolocation-editor.component.ts

@ -25,11 +25,6 @@ export const SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
interface Geolocation {
latitude: number;
longitude: number;
address1: string;
address2: string;
city: string;
state: string;
zip: string;
}
@Component({
@ -53,19 +48,6 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
public geolocationForm =
this.formBuilder.group({
address1: ['', []],
address2: ['', []],
city: ['', []],
state: [
'', [
ValidatorsEx.pattern('[A-Z]{2}', 'This field must be a valid state abbreviation.')
]
],
zip: [
'', [
ValidatorsEx.pattern('[0-9]{5}(\-[0-9]{4})?', 'This field must be a valid ZIP Code.')
]
],
latitude: [
'',
[
@ -93,7 +75,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
private readonly formBuilder: FormBuilder,
private readonly ctx: AppContext
) {
this.useGoogleMaps = this.ctx.app.geocoderKey !== '';
this.useGoogleMaps = this.ctx.uiSettings.geocoderKey !== '';
}
public writeValue(value: Geolocation) {
@ -109,7 +91,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
}
public setDisabledState(isDisabled: boolean): void {
if (this.ctx.app.geocoderKey === '') {
if (!this.useGoogleMaps) {
this.setDisabledStateOSM(isDisabled);
} else {
this.setDisabledStateGoogle(isDisabled);
@ -234,12 +216,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
this.value = {
latitude: latlng.lat,
longitude: latlng.lng,
address1: '',
address2: '',
city: '',
state: '',
zip: ''
longitude: latlng.lng
};
this.updateMarker(false, true);
@ -259,7 +236,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
}
private ngAfterViewInitGoogle() {
this.resourceLoader.loadScript(`https://maps.googleapis.com/maps/api/js?key=${this.ctx.app.geocoderKey}&libraries=places`).then(
this.resourceLoader.loadScript(`https://maps.googleapis.com/maps/api/js?key=${this.ctx.uiSettings.geocoderKey}&libraries=places`).then(
() => {
this.map = new google.maps.Map(this.editor.nativeElement,
{
@ -277,12 +254,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
if (!this.isDisabled) {
this.value = {
latitude: event.latLng.lat(),
longitude: event.latLng.lng(),
address1: this.value == null ? '' : this.value.address1,
address2: this.value == null ? '' : this.value.address2,
city: this.value == null ? '' : this.value.city,
state: this.value == null ? '' : this.value.state,
zip: this.value == null ? '' : this.value.zip
longitude: event.latLng.lng()
};
this.updateMarker(false, true);
@ -305,7 +277,10 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
}
if (!this.isDisabled) {
this.value = this.parseAddress(place);
let latitude = place.geometry.location.lat();
let longitude = place.geometry.location.lng();
this.value = { latitude: latitude, longitude: longitude };
this.updateMarker(false, true);
}
@ -330,40 +305,8 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
this.updateMarker(true, true);
}
private parseAddress(value: any): Geolocation {
let latitude = value.geometry.location.lat();
let longitude = value.geometry.location.lng();
let address1 = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('street_number') > -1))
+ ' ' + this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('route') > -1));
let address2 = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('subpremise') > -1));
let city = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('locality') > -1));
let state = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('administrative_area_level_1') > -1)).toUpperCase();
let zipCode = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('postal_code') > -1));
let zipCodeSuffix = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('postal_code_suffix') > -1));
let zip = zipCodeSuffix === '' ? zipCode : zipCode + '-' + zipCodeSuffix;
return { latitude: latitude, longitude: longitude, address1: address1, address2: address2, city: city, state: state, zip: zip };
}
private getAddressValue(value: any) {
return value == null ? '' : value.short_name;
}
private fillMissingParameters() {
return {
latitude: this.value.latitude,
longitude: this.value.longitude,
address1: this.value.address1 ? this.value.address1 : '',
address2: this.value.address2 ? this.value.address2 : '',
city: this.value.city ? this.value.city : '',
state: this.value.state ? this.value.state : '',
zip: this.value.zip ? this.value.zip : ''
};
}
private updateMarker(zoom: boolean, fireEvent: boolean) {
if (this.ctx.app.geocoderKey === '') {
if (!this.useGoogleMaps) {
this.updateMarkerOSM(zoom, fireEvent);
} else {
this.updateMarkerGoogle(zoom, fireEvent);
@ -380,12 +323,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
this.value = {
latitude: latlng.lat,
longitude: latlng.lng,
address1: '',
address2: '',
city: '',
state: '',
zip: '' };
longitude: latlng.lng};
});
this.marker.on('dragend', () => {
@ -407,7 +345,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
this.marker.setLatLng(latLng);
this.geolocationForm.setValue(this.fillMissingParameters(), { emitEvent: false, onlySelf: false });
this.geolocationForm.setValue(this.value, { emitEvent: false, onlySelf: false });
} else {
if (this.marker) {
this.marker.removeFrom(this.map);
@ -438,12 +376,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
if (!this.isDisabled) {
this.value = {
latitude: event.latLng.lat(),
longitude: event.latLng.lng(),
address1: this.value == null ? '' : this.value.address1,
address2: this.value == null ? '' : this.value.address2,
city: this.value == null ? '' : this.value.city,
state: this.value == null ? '' : this.value.state,
zip: this.value == null ? '' : this.value.zip
longitude: event.latLng.lng()
};
}
});
@ -451,12 +384,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
if (!this.isDisabled) {
this.value = {
latitude: event.latLng.lat(),
longitude: event.latLng.lng(),
address1: this.value == null ? '' : this.value.address1,
address2: this.value == null ? '' : this.value.address2,
city: this.value == null ? '' : this.value.city,
state: this.value == null ? '' : this.value.state,
zip: this.value == null ? '' : this.value.zip
longitude: event.latLng.lng()
};
this.updateMarker(false, true);
@ -475,7 +403,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
this.marker.setPosition(latLng);
this.map.setZoom(16);
this.geolocationForm.setValue(this.fillMissingParameters(), { emitEvent: false, onlySelf: false });
this.geolocationForm.setValue(this.value, { emitEvent: false, onlySelf: false });
} else {
if (this.marker) {
this.marker.setMap(null);

50
src/Squidex/app/shared/services/apps-store.service.spec.ts

@ -13,30 +13,39 @@ import {
AppsService,
AppsStoreService,
CreateAppDto,
DateTime
DateTime,
UIService,
UISettingsDto
} from './../';
describe('AppsStoreService', () => {
const now = DateTime.now();
const oldApps = [
new AppDto('id', 'old-name', 'Owner', now, now, 'Free', 'Plan', ''),
new AppDto('id', 'old-name', 'Owner', now, now, 'Free', 'Plan', '')
new AppDto('id', 'old-name', 'Owner', now, now, 'Free', 'Plan'),
new AppDto('id', 'old-name', 'Owner', now, now, 'Free', 'Plan')
];
const newApp = new AppDto('id', 'new-name', 'Owner', now, now, 'Free', 'Plan', '');
const newApp = new AppDto('id', 'new-name', 'Owner', now, now, 'Free', 'Plan');
const settings: UISettingsDto = { regexSuggestions: [], geocoderKey: '' };
let appsService: IMock<AppsService>;
let uiService: IMock<UIService>;
beforeEach(() => {
appsService = Mock.ofType(AppsService);
});
uiService = Mock.ofType(UIService);
it('should load automatically', () => {
appsService.setup(x => x.getApps())
.returns(() => Observable.of(oldApps))
.verifiable(Times.once());
uiService.setup(x => x.getSettings())
.returns(() => Observable.of(settings))
.verifiable(Times.once());
});
const store = new AppsStoreService(appsService.object);
it('should load automatically', () => {
const store = new AppsStoreService(appsService.object, uiService.object);
let result1: AppDto[] | null = null;
let result2: AppDto[] | null = null;
@ -56,15 +65,11 @@ describe('AppsStoreService', () => {
});
it('should add app to cache when created', () => {
appsService.setup(x => x.getApps())
.returns(() => Observable.of(oldApps))
.verifiable(Times.once());
appsService.setup(x => x.postApp(It.isAny()))
.returns(() => Observable.of(newApp))
.verifiable(Times.once());
const store = new AppsStoreService(appsService.object);
const store = new AppsStoreService(appsService.object, uiService.object);
let result1: AppDto[] | null = null;
let result2: AppDto[] | null = null;
@ -86,11 +91,7 @@ describe('AppsStoreService', () => {
});
it('should select app', (done) => {
appsService.setup(x => x.getApps())
.returns(() => Observable.of(oldApps))
.verifiable(Times.once());
const store = new AppsStoreService(appsService.object);
const store = new AppsStoreService(appsService.object, uiService.object);
store.selectApp('old-name').subscribe(isSelected => {
expect(isSelected).toBeTruthy();
@ -104,4 +105,19 @@ describe('AppsStoreService', () => {
done();
});
});
it('should load uisettings',
() => {
const store = new AppsStoreService(appsService.object, uiService.object);
store.uiSettings.subscribe(result => {
expect(result).toEqual(settings);
uiService.verifyAll();
},
err => {
expect(err).toBeNull();
});
});
});

18
src/Squidex/app/shared/services/apps-store.service.ts

@ -16,10 +16,13 @@ import {
CreateAppDto
} from './apps.service';
import { UIService, UISettingsDto } from './ui.service';
@Injectable()
export class AppsStoreService {
private readonly apps$ = new ReplaySubject<AppDto[]>(1);
private readonly app$ = new BehaviorSubject<AppDto | null>(null);
private readonly uiSettings$ = new BehaviorSubject<UISettingsDto | null>(null);
public get apps(): Observable<AppDto[]> {
return this.apps$;
@ -29,8 +32,13 @@ export class AppsStoreService {
return this.app$;
}
public get uiSettings(): Observable<UISettingsDto | null> {
return this.uiSettings$;
}
constructor(
private readonly appsService: AppsService
private readonly appsService: AppsService,
private readonly uiService: UIService
) {
if (!appsService) {
return;
@ -42,6 +50,14 @@ export class AppsStoreService {
}, error => {
this.apps$.next([]);
});
this.uiService.getSettings()
.subscribe(settings => {
this.uiSettings$.next(settings);
},
error => {
this.uiSettings$.next(null);
});
}
public selectApp(name: string | null): Observable<boolean> {

15
src/Squidex/app/shared/services/apps.service.spec.ts

@ -59,8 +59,7 @@ describe('AppsService', () => {
created: '2016-01-01',
lastModified: '2016-02-02',
planName: 'Free',
planUpgrade: 'Basic',
geocoderKey: ''
planUpgrade: 'Basic'
},
{
id: '456',
@ -69,14 +68,13 @@ describe('AppsService', () => {
created: '2017-01-01',
lastModified: '2017-02-02',
planName: 'Basic',
planUpgrade: 'Enterprise',
geocoderKey: ''
planUpgrade: 'Enterprise'
}
]);
expect(apps).toEqual([
new AppDto('123', 'name1', 'Owner', DateTime.parseISO('2016-01-01'), DateTime.parseISO('2016-02-02'), 'Free', 'Basic', ''),
new AppDto('456', 'name2', 'Owner', DateTime.parseISO('2017-01-01'), DateTime.parseISO('2017-02-02'), 'Basic', 'Enterprise', '')
new AppDto('123', 'name1', 'Owner', DateTime.parseISO('2016-01-01'), DateTime.parseISO('2016-02-02'), 'Free', 'Basic'),
new AppDto('456', 'name2', 'Owner', DateTime.parseISO('2017-01-01'), DateTime.parseISO('2017-02-02'), 'Basic', 'Enterprise')
]);
}));
@ -100,10 +98,9 @@ describe('AppsService', () => {
id: '123',
permission: 'Reader',
planName: 'Basic',
planUpgrade: 'Enterprise',
geocoderKey: ''
planUpgrade: 'Enterprise'
});
expect(app).toEqual(new AppDto('123', dto.name, 'Reader', now, now, 'Basic', 'Enterprise', ''));
expect(app).toEqual(new AppDto('123', dto.name, 'Reader', now, now, 'Basic', 'Enterprise'));
}));
});

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

@ -26,8 +26,7 @@ export class AppDto {
public readonly created: DateTime,
public readonly lastModified: DateTime,
public readonly planName: string,
public readonly planUpgrade: string,
public readonly geocoderKey: string
public readonly planUpgrade: string
) {
}
}
@ -65,8 +64,7 @@ export class AppsService {
DateTime.parseISO(item.created),
DateTime.parseISO(item.lastModified),
item.planName,
item.planUpgrade,
item.geocoderKey);
item.planUpgrade);
});
})
.pretifyError('Failed to load apps. Please reload.');
@ -81,7 +79,7 @@ export class AppsService {
now = now || DateTime.now();
return new AppDto(body.id, dto.name, body.permission, now, now, body.planName, body.planUpgrade, body.geocoderKey);
return new AppDto(body.id, dto.name, body.permission, now, now, body.planName, body.planUpgrade);
})
.do(() => {
this.analytics.trackEvent('App', 'Created', dto.name);

2
src/Squidex/app/shared/services/ui.service.spec.ts

@ -41,7 +41,7 @@ describe('UIService', () => {
settings1 = result;
});
const response: UISettingsDto = { regexSuggestions: [] };
const response: UISettingsDto = { regexSuggestions: [], geocoderKey: '' };
const req = httpMock.expectOne('http://service/p/api/ui/settings');

3
src/Squidex/app/shared/services/ui.service.ts

@ -15,6 +15,7 @@ import { ApiUrlConfig } from 'framework';
export interface UISettingsDto {
regexSuggestions: UIRegexSuggestionDto[];
geocoderKey: string;
}
export interface UIRegexSuggestionDto {
@ -39,7 +40,7 @@ export class UIService {
return this.http.get<UISettingsDto>(url)
.catch(error => {
return Observable.of({ regexSuggestions: [] });
return Observable.of({ regexSuggestions: [], geocoderKey: '' });
})
.do(settings => {
this.settings = settings;

32
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/GeolocationFieldTests.cs

@ -97,38 +97,6 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
new[] { "<FIELD> is not a valid value. Geolocation must have proper properties." });
}
[Fact]
public async Task Should_add_errors_if_geolocation_has_invalid_zip()
{
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties { IsRequired = true });
var geolocation = new JObject(
new JProperty("latitude", 0),
new JProperty("longitude", 0),
new JProperty("zip", "1234"));
await sut.ValidateAsync(CreateValue(geolocation), errors);
errors.ShouldBeEquivalentTo(
new[] { "<FIELD> is not a valid value. ZIP Code must match postal code with optional suffix pattern." });
}
[Fact]
public async Task Should_add_errors_if_geolocation_has_invalid_state()
{
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties { IsRequired = true });
var geolocation = new JObject(
new JProperty("latitude", 0),
new JProperty("longitude", 0),
new JProperty("state", "1"));
await sut.ValidateAsync(CreateValue(geolocation), errors);
errors.ShouldBeEquivalentTo(
new[] { "<FIELD> is not a valid value. State must be two capital letters." });
}
[Fact]
public async Task Should_add_errors_if_geolocation_is_required()
{

2
tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandMiddlewareTests.cs

@ -66,7 +66,7 @@ namespace Squidex.Domain.Apps.Write.Contents
private readonly NamedContentData invalidGoogleMapsLatLongData = new NamedContentData()
.AddField("my-geolocation-field1", new ContentFieldData()
.AddValue(JObject.FromObject(new { latitude = 0, longitude = (double?)null, address1 = "baddata" })));
.AddValue(JObject.FromObject(new { latitude = 0, longitude = (double?)null, address = "baddata" })));
public ContentCommandMiddlewareTests()
{

Loading…
Cancel
Save