API: Use noOptions for parameters
Move config.service to _services/ Rationalise bootstrap and conf (WIP) Fix auth: - Add HTTP interceptor - Bootstrap after login/logout Some cleanups
This commit is contained in:
parent
8593d0b5cd
commit
4eb4c0d0dd
29 changed files with 223 additions and 296 deletions
24
src/app/_helpers/http.interceptor.ts
Normal file
24
src/app/_helpers/http.interceptor.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS } from '@angular/common/http'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
@Injectable()
|
||||
export class AuthInterceptor implements HttpInterceptor {
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
const token = localStorage.token
|
||||
|
||||
if (!token) {
|
||||
return next.handle(req)
|
||||
}
|
||||
|
||||
req = req.clone({
|
||||
headers: req.headers.set('Authorization', `Bearer ${token}`),
|
||||
})
|
||||
|
||||
return next.handle(req)
|
||||
}
|
||||
}
|
||||
|
||||
export const httpInterceptorProviders = [
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
||||
]
|
|
@ -1,16 +1,18 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http'
|
||||
import { Observable, BehaviorSubject, from, throwError } from 'rxjs'
|
||||
import { Observable, BehaviorSubject, from, throwError, of } from 'rxjs'
|
||||
import { map, catchError } from 'rxjs/operators'
|
||||
|
||||
import { User } from '../_models/user'
|
||||
import { RoleReadNoUsers } from '../openapi'
|
||||
import { RoleReadNoUsers, ApiService, Token } from '../openapi'
|
||||
import { BootstrapService } from './bootstrap.service'
|
||||
import { ConfigService } from './config.service'
|
||||
|
||||
interface AuthResponse {
|
||||
access_token: string,
|
||||
roles: string[]
|
||||
}
|
||||
// interface AuthResponse {
|
||||
// access_token: string,
|
||||
// roles: string[]
|
||||
// }
|
||||
|
||||
@Injectable()
|
||||
export class AuthenticationService {
|
||||
|
@ -20,6 +22,9 @@ export class AuthenticationService {
|
|||
|
||||
constructor(
|
||||
private _http: HttpClient,
|
||||
public api: ApiService,
|
||||
public bootstrapService: BootstrapService,
|
||||
public configService: ConfigService,
|
||||
) {
|
||||
// set token if saved in local storage
|
||||
this.user.next(<User>JSON.parse(localStorage.getItem('user')))
|
||||
|
@ -54,43 +59,44 @@ export class AuthenticationService {
|
|||
)
|
||||
}
|
||||
|
||||
login(userName: string, password: string): Observable<AuthResponse> {
|
||||
login(username: string, password: string): Observable<Token> {
|
||||
const headers = new HttpHeaders({'Content-Type': 'application/x-www-form-urlencoded'})
|
||||
var formData: any = new URLSearchParams()
|
||||
formData.set('username', userName)
|
||||
formData.set('password', password)
|
||||
return this._http.post<AuthResponse>(
|
||||
'/api/token', formData, {headers: headers}
|
||||
).pipe(map(
|
||||
(response: AuthResponse) => {
|
||||
// login successful if there's a jwt token in the response
|
||||
let token = response.access_token
|
||||
if (token) {
|
||||
//const decodedToken = this.helper.decodeToken(token)
|
||||
// store userName and jwt token in local storage to keep user logged in between page refreshes
|
||||
localStorage.setItem('user',
|
||||
JSON.stringify({
|
||||
userName: userName,
|
||||
token: token,
|
||||
roles: response.roles,
|
||||
})
|
||||
)
|
||||
// var formData: any = new URLSearchParams()
|
||||
// formData.set('username', userName)
|
||||
// formData.set('password', password)
|
||||
return this.api.loginForAccessTokenApiTokenPost({
|
||||
username: username,
|
||||
password: password
|
||||
}).pipe(map(
|
||||
token => {
|
||||
localStorage.setItem('token', token.access_token)
|
||||
// store jwt token in local storage to keep user logged in between page refreshes
|
||||
// localStorage.setItem('user',
|
||||
// JSON.stringify({
|
||||
// userName: username,
|
||||
// token: token,
|
||||
// roles: response.roles,
|
||||
// })
|
||||
// )
|
||||
|
||||
console.log('TODO: AuthenticationService roles to be set by refreshing bootstrap')
|
||||
// this.roles = response.roles
|
||||
this.bootstrapService.get().subscribe(
|
||||
bsData => this.configService.setConf(bsData)
|
||||
)
|
||||
return token
|
||||
// this.roles = response.roles
|
||||
|
||||
// Notify
|
||||
this.user.next(new User(userName, token))
|
||||
// Notify
|
||||
// this.user.next(new User(userName, token))
|
||||
|
||||
// return true to indicate successful login
|
||||
// return true
|
||||
} else {
|
||||
this.user.next(undefined)
|
||||
this.roles = []
|
||||
// return false to indicate failed login
|
||||
// return false
|
||||
}
|
||||
return response
|
||||
// return true to indicate successful login
|
||||
// return true
|
||||
// } else {
|
||||
// this.user.next(undefined)
|
||||
// this.roles = []
|
||||
// // return false to indicate failed login
|
||||
// // return false
|
||||
// }
|
||||
// return response
|
||||
}
|
||||
))
|
||||
}
|
||||
|
@ -99,24 +105,32 @@ export class AuthenticationService {
|
|||
// XXX: not completly safe: the server might be down:
|
||||
// We should actually *check* that the logout response is OK and display message
|
||||
// clear token remove user from local storage to log user out
|
||||
let has_token: boolean = this.user.value && !!this.user.value.token
|
||||
localStorage.removeItem('user')
|
||||
this.user.next(undefined)
|
||||
this.roles = []
|
||||
// let has_token: boolean = this.user.value && !!this.user.value.token
|
||||
const has_token: boolean = !!localStorage.getItem('token')
|
||||
// localStorage.removeItem('user')
|
||||
// this.user.next(undefined)
|
||||
// this.roles = []
|
||||
|
||||
// Tell server that the user has logged out
|
||||
if (has_token) {
|
||||
this._http.get('/auth/logout').subscribe(response => {})
|
||||
this._http.get('/api/logout').subscribe(response => {})
|
||||
localStorage.removeItem('token')
|
||||
}
|
||||
this.bootstrapService.get().subscribe(
|
||||
bsData => this.configService.setConf(bsData)
|
||||
)
|
||||
return has_token
|
||||
}
|
||||
|
||||
logoutAdmin(): void {
|
||||
}
|
||||
|
||||
isAuthorized(roles: string[]) {
|
||||
isAuthorized(roles: string[]): Observable<boolean> {
|
||||
// Return true if at least one role in given list matches one role of the authenticated user
|
||||
if (roles.length == 0) return true
|
||||
return this.roles.filter(value => -1 !== roles.indexOf(value.name)).length > 0
|
||||
if (roles.length == 0) return of(true)
|
||||
// return this.roles.filter(value => -1 !== roles.indexOf(value.name)).length > 0
|
||||
return this.configService.conf.pipe(map(
|
||||
conf => conf.bsData?.user.roles.filter(value => -1 !== roles.indexOf(value.name)).length > 0
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
import { Observable } from 'rxjs'
|
||||
import { Observable, map } from 'rxjs'
|
||||
|
||||
import { ApiService, BootstrapData } from '../openapi'
|
||||
|
||||
|
|
|
@ -2,23 +2,25 @@ import { inject, Injectable } from "@angular/core"
|
|||
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from "@angular/router"
|
||||
import { Subject, ReplaySubject, BehaviorSubject, Observable, map } from "rxjs"
|
||||
|
||||
import { BootstrapData } from "../openapi"
|
||||
import { BootstrapService } from "./bootstrap.service"
|
||||
|
||||
export class Config {
|
||||
map = {}
|
||||
proj = {}
|
||||
measures = {}
|
||||
geo = {}
|
||||
constructor(
|
||||
public bsData?: BootstrapData
|
||||
) {}
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ConfigService {
|
||||
defaultConf: Config = {
|
||||
'map': {},
|
||||
'proj': {},
|
||||
'measures': {},
|
||||
'geo': {}
|
||||
}
|
||||
// defaultConf: Config = {
|
||||
// 'map': {},
|
||||
// 'proj': {},
|
||||
// 'measures': {},
|
||||
// 'geo': {}
|
||||
// }
|
||||
|
||||
hasConf = new ReplaySubject<undefined>()
|
||||
public conf: BehaviorSubject<Config> = new BehaviorSubject<Config>(new Config())
|
||||
|
@ -36,12 +38,14 @@ export class ConfigService {
|
|||
}
|
||||
*/
|
||||
|
||||
setConf(c: Object) {
|
||||
this.conf.value.map = c['map']
|
||||
this.conf.value.proj = c['proj']
|
||||
this.conf.value.geo = c['geo']
|
||||
this.conf.value.measures = c['measures']
|
||||
this.conf.next(this.conf.value)
|
||||
setConf(bsData: BootstrapData) {
|
||||
this.conf.next(new Config(bsData))
|
||||
// this.conf.value.map = c.
|
||||
// this.conf.value.proj = c['proj']
|
||||
// this.conf.value.geo = c['geo']
|
||||
// this.conf.value.measures = c['measures']
|
||||
// this.conf.next(this.conf.value)
|
||||
localStorage.setItem('bsData', JSON.stringify(bsData))
|
||||
this.hasConf.next(undefined)
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ export class DataService {
|
|||
}
|
||||
|
||||
getList(store: string): Observable<MeasuresItem[]> {
|
||||
return this.api.getModelListApiDataProviderStoreGet({store: store})
|
||||
return this.api.getModelListApiDataProviderStoreGet(store)
|
||||
}
|
||||
|
||||
getValues(
|
||||
|
@ -61,11 +61,8 @@ export class DataService {
|
|||
// .set('resample', sampling)
|
||||
// .set('format', format)
|
||||
// FIXME: add the name of the value to fetch
|
||||
return this.api.getModelValuesApiStoreNameValuesValueGet({
|
||||
storeName: store,
|
||||
value: value,
|
||||
where: JSON.stringify(p),
|
||||
resample: sampling
|
||||
})
|
||||
return this.api.getModelValuesApiStoreNameValuesValueGet(
|
||||
store, value, JSON.stringify(p), sampling
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,9 +34,7 @@ export class GeoJsonService {
|
|||
}
|
||||
|
||||
getStyle(store: string): Observable<MaplibreStyle> {
|
||||
return this.mapService.getLayerStyleApiMapLayerStyleStoreGet(
|
||||
{store: store}
|
||||
)
|
||||
return this.mapService.getLayerStyleApiMapLayerStyleStoreGet(store)
|
||||
}
|
||||
|
||||
getAll(url: string, store: string, params?: object): Observable<MapboxDataAndStyle> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { configResolver } from './config.service'
|
||||
import { configResolver } from './_services/config.service'
|
||||
import { LoginComponent } from './login/login.component'
|
||||
import { PageNotFoundComponent } from './pageNotFound.component'
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<mat-toolbar fxFlex="2em" id='top-toolbar'>
|
||||
<span
|
||||
style='font-family:GisafSymbols'
|
||||
matTooltip="Gisaf v. {{ version }}"
|
||||
matTooltip="Gisaf v. {{ (configService.conf | async).bsData?.version }}"
|
||||
matTooltipPosition="below"
|
||||
class='gisafIcon'
|
||||
>
|
||||
|
@ -25,12 +25,12 @@
|
|||
</a>
|
||||
</nav>
|
||||
<span class="fill-space"></span>
|
||||
<span *ngIf="authenticationService.user$ | async; else login" class="auth-indicator">
|
||||
<span *ngIf="(configService.conf | async).bsData?.user; else login" class="auth-indicator">
|
||||
<nav>
|
||||
<a mat-icon-button
|
||||
(click)='this.authenticationService.logout()'
|
||||
aria-label="Log out"
|
||||
matTooltip="User: {{ (authenticationService.user$ | async ).userName }}. Click to log out">
|
||||
matTooltip="User: {{ (configService.conf | async ).bsData?.user.username }}. Click to log out">
|
||||
<mat-icon>verified_user</mat-icon>
|
||||
</a>
|
||||
</nav>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Component, OnInit,
|
|||
ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'
|
||||
import { Title } from '@angular/platform-browser'
|
||||
import { BootstrapService } from './_services/bootstrap.service'
|
||||
import { ConfigService } from './config.service'
|
||||
import { ConfigService } from './_services/config.service'
|
||||
import { MatSnackBar } from '@angular/material/snack-bar'
|
||||
|
||||
import { AuthenticationService } from './_services/authentication.service'
|
||||
|
@ -52,7 +52,6 @@ export class AppComponent implements OnInit {
|
|||
this.title = res.title || this.title
|
||||
this.titleService.setTitle(res.windowTitle || this.title)
|
||||
this.configService.setConf(res)
|
||||
this.authenticationService.roles = res.user?.roles || []
|
||||
if (res.redirect && (window != window.top)) {
|
||||
// Refusing to be embedded in an iframe
|
||||
let loc = res.redirect + window.location.pathname
|
||||
|
@ -69,10 +68,5 @@ export class AppComponent implements OnInit {
|
|||
)
|
||||
}
|
||||
})
|
||||
|
||||
this.authenticationService.isLoggedIn().subscribe({
|
||||
next: resp => resp,
|
||||
error: (err: string) => this.snackBar.open(err, 'OK', {duration: 3000})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,17 @@
|
|||
import { BrowserModule } from '@angular/platform-browser'
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
||||
import { Injector, NgModule, LOCALE_ID } from '@angular/core'
|
||||
import { NgModule, LOCALE_ID } from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { HttpClientModule } from '@angular/common/http'
|
||||
|
||||
import { MatButtonModule } from '@angular/material/button'
|
||||
import { MatIconModule } from '@angular/material/icon'
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar'
|
||||
import { MatSnackBar } from '@angular/material/snack-bar'
|
||||
import { MatToolbarModule } from '@angular/material/toolbar'
|
||||
import { MatTooltipModule } from '@angular/material/tooltip'
|
||||
|
||||
import { FlexLayoutModule } from 'ngx-flexible-layout'
|
||||
|
||||
// import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular'
|
||||
// import { HttpLink } from 'apollo-angular/http'
|
||||
// import { onError } from '@apollo/client/link/error'
|
||||
// import { split, from, ApolloLink, InMemoryCache, ApolloClientOptions } from '@apollo/client/core'
|
||||
// import { WebSocketLink } from '@apollo/client/link/ws'
|
||||
// import { getMainDefinition, getOperationName } from '@apollo/client/utilities'
|
||||
// import { DefinitionNode } from 'graphql'
|
||||
|
||||
import { AppComponent } from './app.component'
|
||||
import { PageNotFoundComponent } from './pageNotFound.component'
|
||||
import { LoginModule } from './login/login.module'
|
||||
|
@ -28,18 +19,18 @@ import { AuthenticationService } from './_services/authentication.service'
|
|||
import { BootstrapService } from './_services/bootstrap.service'
|
||||
import { ModelDataService } from './_services/apollo.service'
|
||||
import { ActionsService } from './_services/actions.service'
|
||||
import { ConfigService } from './config.service'
|
||||
import { ConfigService } from './_services/config.service'
|
||||
|
||||
import { ApiService } from './openapi/services/ApiService'
|
||||
import { AdminService } from './openapi/services/AdminService'
|
||||
import { DashboardService } from './openapi/services/DashboardService'
|
||||
import { GeoapiService } from './openapi/services/GeoapiService'
|
||||
import { MapService } from './openapi/services/MapService'
|
||||
import { httpInterceptorProviders } from './_helpers/http.interceptor'
|
||||
|
||||
import { HtmlSnackbarComponent } from './custom-snackbar/custom-snackbar.component'
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module'
|
||||
import { environment } from '../environments/environment'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -79,95 +70,11 @@ import { environment } from '../environments/environment'
|
|||
MapService,
|
||||
ConfigService,
|
||||
ModelDataService,
|
||||
httpInterceptorProviders,
|
||||
{
|
||||
provide: LOCALE_ID,
|
||||
useValue: "en-IN"
|
||||
},
|
||||
// {
|
||||
// provide: APOLLO_OPTIONS,
|
||||
// useFactory(httpLink: HttpLink, snackBar: MatSnackBar) {
|
||||
// const definitionIsMutation = (d: DefinitionNode) => {
|
||||
// return d.kind === 'OperationDefinition' && d.operation === 'mutation'
|
||||
// }
|
||||
|
||||
// // See https://github.com/apollographql/apollo-angular/issues/1013
|
||||
// const linkQueries = httpLink.create({
|
||||
// uri: '/graphql',
|
||||
// method: 'GET',
|
||||
// })
|
||||
|
||||
// const linkMutations = httpLink.create({
|
||||
// uri: '/graphql',
|
||||
// })
|
||||
|
||||
// const splittedLink = split(
|
||||
// ({ query }) => query.definitions.some(definitionIsMutation),
|
||||
// linkMutations,
|
||||
// linkQueries,
|
||||
// )
|
||||
|
||||
// const schedulerQueriesLink = httpLink.create({
|
||||
// uri: '/graphql_sched',
|
||||
// method: 'GET',
|
||||
// })
|
||||
|
||||
// const proxyLink = split(
|
||||
// ({ query }) => {
|
||||
// let res = query.definitions[0]['name']['value'].startsWith('scheduler_')
|
||||
// return res
|
||||
// },
|
||||
// schedulerQueriesLink,
|
||||
// splittedLink
|
||||
// )
|
||||
|
||||
// const errorLink = onError(({ graphQLErrors, networkError }) => {
|
||||
// if (graphQLErrors)
|
||||
// graphQLErrors.map(({ message, locations, path }) => {
|
||||
// snackBar.open(`Error: ${message}`, 'close')
|
||||
// console.error(
|
||||
// `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
|
||||
// graphQLErrors
|
||||
// )
|
||||
// }
|
||||
// )
|
||||
// if (networkError) {
|
||||
// snackBar.open(
|
||||
// `Network error: ${networkError['statusText']}`,
|
||||
// 'close',
|
||||
// )
|
||||
// console.error(networkError)
|
||||
// }
|
||||
// })
|
||||
|
||||
// const httpLinkWithErrorHandling = from([
|
||||
// errorLink,
|
||||
// proxyLink,
|
||||
// ])
|
||||
|
||||
// return {
|
||||
// link: httpLinkWithErrorHandling,
|
||||
// cache: new InMemoryCache(),
|
||||
// defaultOptions: {
|
||||
// watchQuery: {
|
||||
// fetchPolicy: 'network-only',
|
||||
// errorPolicy: 'ignore',
|
||||
// },
|
||||
// query: {
|
||||
// fetchPolicy: 'network-only',
|
||||
// errorPolicy: 'all',
|
||||
// },
|
||||
// subscription: {
|
||||
// fetchPolicy: 'network-only',
|
||||
// errorPolicy: 'all',
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
// deps: [
|
||||
// HttpLink,
|
||||
// MatSnackBar
|
||||
// ],
|
||||
// }
|
||||
],
|
||||
bootstrap: [
|
||||
AppComponent
|
||||
|
|
|
@ -10,7 +10,7 @@ import { MapControlService } from '../map/map-control.service'
|
|||
import { LayerNode } from '../map/models'
|
||||
import { Tag, TagAction } from './info-tags/tags.service'
|
||||
|
||||
import { ConfigService } from '../config.service'
|
||||
import { ConfigService } from '../_services/config.service'
|
||||
import { DataService } from '../_services/data.service'
|
||||
import { ApiService, ModelInfo, FeatureInfo, PlotParams } from '../openapi'
|
||||
|
||||
|
@ -466,7 +466,7 @@ export class InfoDataService {
|
|||
public taggedLayers$ = this.taggedLayers.asObservable()
|
||||
|
||||
getModelInfo(store: string): Observable<ModelInfo> {
|
||||
return this.api.getModelInfoApiModelInfoStoreGet({store: store})
|
||||
return this.api.getModelInfoApiModelInfoStoreGet(store)
|
||||
console.warn('Migrate Graphql')
|
||||
return observableOf()
|
||||
// return this.apollo.query({
|
||||
|
@ -555,11 +555,7 @@ export class InfoDataService {
|
|||
}
|
||||
|
||||
getPlotParams(store: string, id: string, value: string): Observable<PlotParams> {
|
||||
return this.api.getPlotParamsApiPlotParamsStoreGet({
|
||||
'store': store,
|
||||
'id': id,
|
||||
'value': value
|
||||
})
|
||||
return this.api.getPlotParamsApiPlotParamsStoreGet(store, id, value)
|
||||
console.warn('Migrate Graphql')
|
||||
return observableOf()
|
||||
// return this.apollo.query({
|
||||
|
@ -592,10 +588,7 @@ export class InfoDataService {
|
|||
}
|
||||
|
||||
getFeatureInfo(store: string, id: string): Observable<FeatureInfo> {
|
||||
return this.api.getFeatureInfoApiFeatureInfoStoreIdGet({
|
||||
store: store,
|
||||
id: id
|
||||
})
|
||||
return this.api.getFeatureInfoApiFeatureInfoStoreIdGet(store, id)
|
||||
// return this.apollo.query({
|
||||
// query: getFeatureInfoQuery,
|
||||
// variables: {
|
||||
|
@ -710,7 +703,7 @@ export class InfoDataService {
|
|||
}
|
||||
|
||||
public getTagKeys(): Observable<string[]> {
|
||||
return observableOf(this.configService.conf.value.map['tagKeys'])
|
||||
return observableOf(this.configService.conf.value.bsData?.map['tagKeys'])
|
||||
// This could be fetched from the server
|
||||
/*
|
||||
return this.apollo.query({
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Router } from '@angular/router'
|
|||
|
||||
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms'
|
||||
|
||||
import { ConfigService } from '../config.service'
|
||||
import { ConfigService } from '../_services/config.service'
|
||||
import { AuthenticationService } from '../_services/authentication.service'
|
||||
|
||||
@Component({
|
||||
|
@ -41,15 +41,8 @@ export class LoginComponent implements OnInit {
|
|||
this.loading = true
|
||||
this.authenticationService.login(this.formGroup.value.userName, this.formGroup.value.password).subscribe({
|
||||
next: result => {
|
||||
if (result.access_token) {
|
||||
// login successful
|
||||
this.router.navigate(['/'])
|
||||
} else {
|
||||
// login failed
|
||||
this.error = 'User name or password is incorrect'
|
||||
this.loading = false
|
||||
this.cdr.markForCheck()
|
||||
}
|
||||
// login successful
|
||||
this.router.navigate(['/'])
|
||||
},
|
||||
error: error => {
|
||||
console.error(error)
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
step=0.01
|
||||
matTooltip="Opacity of the background layer"
|
||||
matTooltipPosition="right"
|
||||
#ngSlider><input matSliderThumb value="{{ (configService.conf | async).map['opacity'] }}" (input)="mapControlService.baseStyleOpacity.next({source: ngSliderThumb, parent: ngSlider, value: ngSliderThumb.value}.value)" #ngSliderThumb="matSliderThumb" />
|
||||
#ngSlider><input matSliderThumb value="{{ (configService.conf | async).bsData.map.opacity }}" (input)="mapControlService.baseStyleOpacity.next({source: ngSliderThumb, parent: ngSlider, value: ngSliderThumb.value}.value)" #ngSliderThumb="matSliderThumb" />
|
||||
</mat-slider>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
|
@ -85,7 +85,7 @@
|
|||
matTooltip='Filter features from survey categories by status'
|
||||
matTooltipPosition="right"
|
||||
>
|
||||
<mat-option *ngFor="let _status of (configService.conf | async).map['status']" [value]="_status">{{ _status }}</mat-option>
|
||||
<mat-option *ngFor="let _status of (configService.conf | async).bsData?.map.status" [value]="_status">{{ _status }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
@ -147,22 +147,22 @@
|
|||
<mat-menu #toolsMenu="matMenu" class='extended-width'>
|
||||
<button mat-menu-item
|
||||
(click)="downloadSelectedLayers('gpkg')">
|
||||
Download Geopackage of selected layers (EPSG {{ (configService.conf | async).geo['srid'] }})
|
||||
Download Geopackage of selected layers (EPSG {{ (configService.conf | async).bsData?.geo.srid }})
|
||||
</button>
|
||||
<button mat-menu-item [disabled]=true
|
||||
(click)="downloadSelectedLayers('dxf')">
|
||||
Download DXF of selected layers (EPSG {{ (configService.conf | async).geo['srid'] }})
|
||||
Download DXF of selected layers (EPSG {{ (configService.conf | async).bsData?.geo.srid }})
|
||||
</button>
|
||||
<button mat-menu-item [disabled]=true
|
||||
(click)="downloadSelectedLayers('dxf', true)">
|
||||
Download DXF of selected layers (reprojected to EPSG {{ (configService.conf | async).geo['srid_for_proj'] }})
|
||||
Download DXF of selected layers (reprojected to EPSG {{ (configService.conf | async).bsData?.geo.srid_for_proj }})
|
||||
</button>
|
||||
<button mat-menu-item
|
||||
(click)="downloadSelectedLayers('shapefile')">
|
||||
Download Shapefiles of selected layers (EPSG {{ (configService.conf | async).geo['srid'] }}) [<i><b>Deprecated</b>: use Geopackage</i>]
|
||||
Download Shapefiles of selected layers (EPSG {{ (configService.conf | async).bsData?.geo.srid }}) [<i><b>Deprecated</b>: use Geopackage</i>]
|
||||
</button>
|
||||
</mat-menu>
|
||||
<button mat-raised-button [disabled]="!canCreateBaseMap"
|
||||
<button mat-raised-button [disabled]="!(authenticationService.isAuthorized(['base_map_creator']) | async)"
|
||||
matTooltip="Create a base map with the selected layers"
|
||||
(click)="createBaseMap()">
|
||||
Create a base map...
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Component, OnInit } from '@angular/core'
|
|||
import { FormControl } from '@angular/forms'
|
||||
import { MatSnackBar } from '@angular/material/snack-bar'
|
||||
|
||||
import { ConfigService } from '../../config.service'
|
||||
import { ConfigService } from '../../_services/config.service'
|
||||
import { MapControlService } from '../map-control.service'
|
||||
import { TreeLayerItemComponent } from '../tree-layer-item/tree-layer-item.component'
|
||||
import { InfoDataService, TaggedLayer } from '../../info/info-data.service'
|
||||
|
@ -30,11 +30,11 @@ export class MapControlsComponent implements OnInit {
|
|||
|
||||
baseStyles: BaseStyle[]
|
||||
|
||||
baseStyleOpacity: number = this.configService.conf.value.map['opacity']
|
||||
baseStyleOpacity: number = this.configService.conf.value.bsData?.map.opacity
|
||||
|
||||
status = new FormControl([])
|
||||
|
||||
canCreateBaseMap: boolean = false
|
||||
// canCreateBaseMap: boolean = false
|
||||
|
||||
constructor(
|
||||
public snackBar: MatSnackBar,
|
||||
|
@ -42,7 +42,7 @@ export class MapControlsComponent implements OnInit {
|
|||
public mapControlService: MapControlService,
|
||||
protected infoDataService: InfoDataService,
|
||||
public mapDataService: MapDataService,
|
||||
private authenticationService: AuthenticationService,
|
||||
public authenticationService: AuthenticationService,
|
||||
public dialog: MatDialog,
|
||||
) {
|
||||
}
|
||||
|
@ -102,11 +102,11 @@ export class MapControlsComponent implements OnInit {
|
|||
this.mapControlService.status.next(value)
|
||||
}
|
||||
)
|
||||
this.status.setValue(this.configService.conf.value.map['defaultStatus'])
|
||||
this.status.setValue(this.configService.conf.value.bsData?.map['defaultStatus'])
|
||||
|
||||
this.mapControlService.baseStyleOpacity.next(this.baseStyleOpacity)
|
||||
|
||||
this.canCreateBaseMap = this.authenticationService.isAuthorized(['base_map_creator'])
|
||||
// this.canCreateBaseMap = this.authenticationService.isAuthorized(['base_map_creator'])
|
||||
}
|
||||
|
||||
openSnackBar(msg: string):void {
|
||||
|
|
|
@ -16,7 +16,7 @@ import Point from '@mapbox/point-geometry'
|
|||
import * as bbox from '@turf/bbox'
|
||||
|
||||
import { GeoJsonService, LiveGeoJsonService, MapboxDataAndStyle } from '../../_services/geojson.service'
|
||||
import { ConfigService } from '../../config.service'
|
||||
import { ConfigService } from '../../_services/config.service'
|
||||
import { MapControlService } from '../map-control.service'
|
||||
import { LayerNode } from '../models'
|
||||
import { InfoDataService, Feature, TaggedLayer, FeatureWithField, TaggedFeature } from '../../info/info-data.service'
|
||||
|
@ -52,15 +52,15 @@ export class GisafMapboxComponent implements OnInit, OnDestroy {
|
|||
// XXX: temporary, for keeping map-controls.component.ts happy
|
||||
map: Map
|
||||
|
||||
zoom: number = this.configService.conf.value.map['zoom']
|
||||
pitch: number = this.configService.conf.value.map['pitch']
|
||||
lat: number = this.configService.conf.value.map['lat']
|
||||
lng: number = this.configService.conf.value.map['lng']
|
||||
bearing: number = this.configService.conf.value.map['bearing']
|
||||
globalAttribution: string = this.configService.conf.value.map['attribution']
|
||||
zoom: number = this.configService.conf.value.bsData?.map['zoom']
|
||||
pitch: number = this.configService.conf.value.bsData?.map['pitch']
|
||||
lat: number = this.configService.conf.value.bsData?.map['lat']
|
||||
lng: number = this.configService.conf.value.bsData?.map['lng']
|
||||
bearing: number = this.configService.conf.value.bsData?.map['bearing']
|
||||
globalAttribution: string = this.configService.conf.value.bsData?.map['attribution']
|
||||
|
||||
baseStyle: BaseStyle = new BaseStyle('None')
|
||||
protected _baseStyleName: string = this.configService.conf.value.map['style']
|
||||
protected _baseStyleName: string = this.configService.conf.value.bsData?.map['style']
|
||||
protected _bottom: number = 0
|
||||
|
||||
protected canvas: HTMLElement
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
import { Subject, BehaviorSubject, forkJoin } from 'rxjs'
|
||||
|
||||
import { ConfigService } from '../config.service'
|
||||
import { ConfigService } from '../_services/config.service'
|
||||
import { LayerNode } from './models'
|
||||
import { TaggedLayer, Feature, FeatureWithField } from '../info/info-data.service'
|
||||
import { BaseMapWithStores } from '../openapi'
|
||||
|
@ -35,11 +35,11 @@ export class MapControlService {
|
|||
public tempSelection = new BehaviorSubject<Object>({})
|
||||
public search = new Subject<boolean>()
|
||||
public baseMapLayers: BehaviorSubject<Set<string>> = new BehaviorSubject(new Set())
|
||||
public status = new BehaviorSubject<string[]>(this.configService.conf.value.map['defaultStatus'])
|
||||
public status = new BehaviorSubject<string[]>(this.configService.conf.value.bsData?.map['defaultStatus'])
|
||||
public hasLabels = new BehaviorSubject<boolean>(false)
|
||||
public hasTags = new BehaviorSubject<boolean>(false)
|
||||
public layerLoaded = new Subject<LayerNode>()
|
||||
public baseStyleName = new BehaviorSubject<string>(this.configService.conf.value.map['style'])
|
||||
public baseStyleName = new BehaviorSubject<string>(this.configService.conf.value.bsData?.map['style'])
|
||||
public addBaseMap = new Subject<BaseMapWithStores>()
|
||||
|
||||
public mapLoaded = new BehaviorSubject<boolean>(false)
|
||||
|
|
|
@ -89,7 +89,7 @@ export class MapDataService {
|
|||
// }
|
||||
|
||||
getBaseStyle(styleName: string): Observable<BaseStyle> {
|
||||
return this.mapService.getBaseStyleApiMapBaseStyleNameGet({name: styleName}).pipe(map(
|
||||
return this.mapService.getBaseStyleApiMapBaseStyleNameGet(styleName).pipe(map(
|
||||
data => new BaseStyle(data.name, <any>data.style)
|
||||
))
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<ng-template matMenuContent let-node="node">
|
||||
<button mat-menu-item (click)="downloadGpkg(node)">
|
||||
<mat-icon>file_download</mat-icon>
|
||||
<span>Download Geopackage (EPSG {{ (configService.conf | async).geo['srid'] }})</span>
|
||||
<span>Download Geopackage (EPSG {{ (configService.conf | async).bsData?.geo.srid_for_proj }})</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
|
@ -55,19 +55,19 @@
|
|||
<ng-template matMenuContent let-node="node">
|
||||
<button mat-menu-item (click)="downloadGpkg(node)">
|
||||
<mat-icon>file_download</mat-icon>
|
||||
<span>Download Geopackage (EPSG {{ (configService.conf | async).proj['srid'] }})</span>
|
||||
<span>Download Geopackage (EPSG {{ (configService.conf | async).bsData?.geo.srid_for_proj }})</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="downloadDXF(node)">
|
||||
<mat-icon>file_download</mat-icon>
|
||||
<span>Download DXF (EPSG {{ (configService.conf | async).proj['srid'] }})</span>
|
||||
<span>Download DXF (EPSG {{ (configService.conf | async).bsData?.geo.proj })</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="downloadDXF(node, true)">
|
||||
<mat-icon>file_download</mat-icon>
|
||||
<span>Download DXF (reprojected to EPSG {{ (configService.conf | async).proj['srid_for_proj'] }})</span>
|
||||
<span>Download DXF (reprojected to EPSG {{ (configService.conf | async).bsData?.geo.srid_for_proj }})</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="downloadLayer(node)">
|
||||
<mat-icon>file_download</mat-icon>
|
||||
<span>Download Shapefile (EPSG {{ (configService.conf | async).proj['srid'] }}) [<i><b>Deprecated</b>: use Geopackage</i>]</span>
|
||||
<span>Download Shapefile (EPSG {{ (configService.conf | async).bsData?.geo.srid }}) [<i><b>Deprecated</b>: use Geopackage</i>]</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="downloadRawData(node)" *ngIf="node.rawSurveyStore">
|
||||
<mat-icon>file_download</mat-icon>
|
||||
|
|
|
@ -10,7 +10,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'
|
|||
|
||||
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'
|
||||
|
||||
import { ConfigService } from '../../config.service'
|
||||
import { ConfigService } from '../../_services/config.service'
|
||||
import { Node, PrimaryGroupNode, LayerNode, TreeData } from '../models'
|
||||
import { MapControlService } from '../map-control.service'
|
||||
import { MapDataService } from '../map-data.service'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'
|
||||
import { ActivatedRoute, Router, ParamMap } from '@angular/router'
|
||||
import { ConfigService } from '../../config.service'
|
||||
import { ConfigService } from '../../_services/config.service'
|
||||
|
||||
@Component({
|
||||
selector: 'gisaf-measures-home',
|
||||
|
@ -14,8 +14,8 @@ export class MeasuresHomeComponent implements OnInit {
|
|||
ngOnInit() {
|
||||
this.configService.conf.subscribe(
|
||||
conf => {
|
||||
if (conf.measures) {
|
||||
this.router.navigate(['/measures', conf.measures['defaultStore']])
|
||||
if (conf.bsData?.measures) {
|
||||
this.router.navigate(['/measures', conf.bsData.measures['defaultStore']])
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Observable, of } from 'rxjs'
|
|||
import { map, startWith, switchMap } from 'rxjs/operators'
|
||||
|
||||
import { DataService } from '../../_services/data.service'
|
||||
import { ConfigService } from '../../config.service'
|
||||
import { ConfigService } from '../../_services/config.service'
|
||||
import { MeasuresItem, DataProvider } from '../../openapi'
|
||||
|
||||
@Component({
|
||||
|
|
|
@ -6,7 +6,7 @@ import { map, startWith, switchMap } from 'rxjs/operators'
|
|||
import { Observable, of } from 'rxjs'
|
||||
|
||||
import { DataService } from '../_services/data.service'
|
||||
import { ConfigService } from '../config.service'
|
||||
import { ConfigService } from '../_services/config.service'
|
||||
import { DataProvider } from '../openapi'
|
||||
|
||||
@Component({
|
||||
|
|
|
@ -21,7 +21,7 @@ export type OpenAPIConfig = {
|
|||
|
||||
export const OpenAPI: OpenAPIConfig = {
|
||||
BASE: '',
|
||||
VERSION: '2023.4.dev37+gb00bf1f.d20240226',
|
||||
VERSION: '2023.4.dev51+g15fe7fa.d20240318',
|
||||
WITH_CREDENTIALS: false,
|
||||
CREDENTIALS: 'include',
|
||||
TOKEN: undefined,
|
||||
|
|
|
@ -29,14 +29,13 @@ export class AdminService {
|
|||
}
|
||||
/**
|
||||
* Get Basket
|
||||
* @param name
|
||||
* @returns AdminBasket Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getBasketApiAdminBasketNameGet({
|
||||
name,
|
||||
}: {
|
||||
public getBasketApiAdminBasketNameGet(
|
||||
name: string,
|
||||
}): Observable<AdminBasket> {
|
||||
): Observable<AdminBasket> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/admin/basket/{name}',
|
||||
|
|
|
@ -43,14 +43,13 @@ export class ApiService {
|
|||
}
|
||||
/**
|
||||
* Login For Access Token
|
||||
* @param formData
|
||||
* @returns Token Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public loginForAccessTokenApiTokenPost({
|
||||
formData,
|
||||
}: {
|
||||
public loginForAccessTokenApiTokenPost(
|
||||
formData: Body_login_for_access_token_api_token_post,
|
||||
}): Observable<Token> {
|
||||
): Observable<Token> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'POST',
|
||||
url: '/api/token',
|
||||
|
@ -62,6 +61,20 @@ export class ApiService {
|
|||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Logout
|
||||
* @returns any Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public logoutApiLogoutGet(): Observable<any> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/logout',
|
||||
errors: {
|
||||
404: `Not found`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get Users
|
||||
* @returns UserRead Successful Response
|
||||
|
@ -154,14 +167,13 @@ export class ApiService {
|
|||
* Json REST store API compatible with Flask Potion and Angular
|
||||
* Get the list of items (used for making the list of items in measures)
|
||||
* Filter only items with at least one measure
|
||||
* @param store
|
||||
* @returns MeasuresItem Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getModelListApiDataProviderStoreGet({
|
||||
store,
|
||||
}: {
|
||||
public getModelListApiDataProviderStoreGet(
|
||||
store: string,
|
||||
}): Observable<Array<MeasuresItem>> {
|
||||
): Observable<Array<MeasuresItem>> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/data-provider/{store}',
|
||||
|
@ -177,20 +189,19 @@ export class ApiService {
|
|||
/**
|
||||
* Get Model Values
|
||||
* Get values
|
||||
* @param storeName
|
||||
* @param value
|
||||
* @param where
|
||||
* @param resample
|
||||
* @returns any Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getModelValuesApiStoreNameValuesValueGet({
|
||||
storeName,
|
||||
value,
|
||||
where,
|
||||
resample,
|
||||
}: {
|
||||
public getModelValuesApiStoreNameValuesValueGet(
|
||||
storeName: string,
|
||||
value: string,
|
||||
where: string,
|
||||
resample?: (string | null),
|
||||
}): Observable<any> {
|
||||
): Observable<any> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/{store_name}/values/{value}',
|
||||
|
@ -252,16 +263,15 @@ export class ApiService {
|
|||
}
|
||||
/**
|
||||
* Get Feature Info
|
||||
* @param store
|
||||
* @param id
|
||||
* @returns any Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getFeatureInfoApiFeatureInfoStoreIdGet({
|
||||
store,
|
||||
id,
|
||||
}: {
|
||||
public getFeatureInfoApiFeatureInfoStoreIdGet(
|
||||
store: string,
|
||||
id: string,
|
||||
}): Observable<(FeatureInfo | null)> {
|
||||
): Observable<(FeatureInfo | null)> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/feature-info/{store}/{id}',
|
||||
|
@ -277,14 +287,13 @@ export class ApiService {
|
|||
}
|
||||
/**
|
||||
* Get Model Info
|
||||
* @param store
|
||||
* @returns ModelInfo Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getModelInfoApiModelInfoStoreGet({
|
||||
store,
|
||||
}: {
|
||||
public getModelInfoApiModelInfoStoreGet(
|
||||
store: string,
|
||||
}): Observable<ModelInfo> {
|
||||
): Observable<ModelInfo> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/model-info/{store}',
|
||||
|
@ -299,18 +308,17 @@ export class ApiService {
|
|||
}
|
||||
/**
|
||||
* Get Plot Params
|
||||
* @param store
|
||||
* @param id
|
||||
* @param value
|
||||
* @returns PlotParams Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getPlotParamsApiPlotParamsStoreGet({
|
||||
store,
|
||||
id,
|
||||
value,
|
||||
}: {
|
||||
public getPlotParamsApiPlotParamsStoreGet(
|
||||
store: string,
|
||||
id: string,
|
||||
value: string,
|
||||
}): Observable<PlotParams> {
|
||||
): Observable<PlotParams> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/plot-params/{store}',
|
||||
|
|
|
@ -45,16 +45,15 @@ export class DashboardService {
|
|||
}
|
||||
/**
|
||||
* Get Dashboard Page
|
||||
* @param group
|
||||
* @param name
|
||||
* @returns DashboardPage_ Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getDashboardPageApiDashboardPageGroupNameGet({
|
||||
group,
|
||||
name,
|
||||
}: {
|
||||
public getDashboardPageApiDashboardPageGroupNameGet(
|
||||
group: string,
|
||||
name: string,
|
||||
}): Observable<DashboardPage_> {
|
||||
): Observable<DashboardPage_> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/dashboard/page/{group}/{name}',
|
||||
|
|
|
@ -17,20 +17,19 @@ export class GeoapiService {
|
|||
* Some REST stores coded manually (route prefixed with "gj": geojson).
|
||||
* :param store_name: name of the model
|
||||
* :return: json
|
||||
* @param storeName
|
||||
* @param ifNoneMatch
|
||||
* @param simplify
|
||||
* @param preserveTopology
|
||||
* @returns any Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getGeojsonApiGjStoreNameGet({
|
||||
storeName,
|
||||
ifNoneMatch,
|
||||
simplify,
|
||||
preserveTopology,
|
||||
}: {
|
||||
public getGeojsonApiGjStoreNameGet(
|
||||
storeName: any,
|
||||
ifNoneMatch?: (string | null),
|
||||
simplify?: (number | null),
|
||||
preserveTopology?: (boolean | null),
|
||||
}): Observable<any> {
|
||||
): Observable<any> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/gj/{store_name}',
|
||||
|
|
|
@ -28,14 +28,13 @@ export class MapService {
|
|||
}
|
||||
/**
|
||||
* Get Base Style
|
||||
* @param name
|
||||
* @returns BaseStyle Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getBaseStyleApiMapBaseStyleNameGet({
|
||||
name,
|
||||
}: {
|
||||
public getBaseStyleApiMapBaseStyleNameGet(
|
||||
name: string,
|
||||
}): Observable<BaseStyle> {
|
||||
): Observable<BaseStyle> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/map/base_style/{name}',
|
||||
|
@ -49,14 +48,13 @@ export class MapService {
|
|||
}
|
||||
/**
|
||||
* Get Layer Style
|
||||
* @param store
|
||||
* @returns any Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getLayerStyleApiMapLayerStyleStoreGet({
|
||||
store,
|
||||
}: {
|
||||
public getLayerStyleApiMapLayerStyleStoreGet(
|
||||
store: string,
|
||||
}): Observable<(MaplibreStyle | null)> {
|
||||
): Observable<(MaplibreStyle | null)> {
|
||||
return __request(OpenAPI, this.http, {
|
||||
method: 'GET',
|
||||
url: '/api/map/layer_style/{store}',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue