refactor(naming): harmonize service file names and API domain prefixes

- Rename hwclan.service.ts → hw-clan.service.ts, hwmember.service.ts → hw-member.service.ts (Angular Style Guide kebab-case)
- Replace _apiVersion (/v1, /v2) with _apiDomain (/skydive, /cms, /herowars) across all feature services
- Update herowars barrel to reference renamed files
- Update CLAUDE.md to reflect corrected path examples
This commit is contained in:
2026-04-26 00:39:03 +02:00
parent 5a7baf4d94
commit 69cf4548d2
14 changed files with 79 additions and 79 deletions
+2 -2
View File
@@ -34,11 +34,11 @@ Resolvers under [src/app/core/resolvers/](src/app/core/resolvers/) pre-fetch dat
### HTTP pipeline
Three interceptors chained in order ([src/app/core/interceptors/](src/app/core/interceptors/)):
1. `apiInterceptor` — prepends `environment.apiBaseUrl` to every request URL. **Consequence:** services call relative paths like `/v2/user`, never absolute URLs.
1. `apiInterceptor` — prepends `environment.apiBaseUrl` to every request URL. **Consequence:** services call relative paths like `/cms/user`, never absolute URLs.
2. `tokenInterceptor` — attaches `Authorization: Token <jwt>` when a token exists.
3. `errorInterceptor` — unwraps `err.error` and rethrows.
`ApiService` ([api.service.ts](src/app/core/services/api.service.ts)) is a thin `HttpClient` wrapper; most feature services call it with a versioned prefix (e.g., `_apiDomain = '/v1'` or `/v2`). The API version is per-service, not global.
`ApiService` ([api.service.ts](src/app/core/services/api.service.ts)) is a thin `HttpClient` wrapper; most feature services call it with a versioned prefix (e.g., `_apiDomain = '/skydive'` or `/cms`). The API version is per-service, not global.
### Auth flow
@@ -8,7 +8,7 @@ import { map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class ApplicationsService {
private _apiVersion = '/v1';
private _apiDomain = '/skydive';
constructor(
private apiService: ApiService
) { }
@@ -23,7 +23,7 @@ export class ApplicationsService {
};
return this.apiService
.get(
`${this._apiVersion}/applications` + ((config.type === 'feed') ? '/feed' : ''),
`${this._apiDomain}/applications` + ((config.type === 'feed') ? '/feed' : ''),
new HttpParams({ fromObject: <{
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
}>params })
@@ -31,22 +31,22 @@ export class ApplicationsService {
}
get(slug: string): Observable<Application> {
return this.apiService.get(`${this._apiVersion}/applications/${slug}`)
return this.apiService.get(`${this._apiDomain}/applications/${slug}`)
.pipe(map(data => data.application));
}
destroy(slug: string): Observable<boolean> {
return this.apiService.delete(`${this._apiVersion}/applications/${slug}`).pipe(map(data => data.deleted));
return this.apiService.delete(`${this._apiDomain}/applications/${slug}`).pipe(map(data => data.deleted));
}
save(application: Application): Observable<Application> {
if (application.slug) {
// If we're updating an existing application
return this.apiService.put(`${this._apiVersion}/applications/${application.slug}`, { application: application })
return this.apiService.put(`${this._apiDomain}/applications/${application.slug}`, { application: application })
.pipe(map(data => data.application));
} else {
// Otherwise, create a new application
return this.apiService.post(`${this._apiVersion}/applications/`, { application: application })
return this.apiService.post(`${this._apiDomain}/applications/`, { application: application })
.pipe(map(data => data.application));
}
}
+12 -12
View File
@@ -8,7 +8,7 @@ import { Article, ArticleList, ArticleListConfig, ArticleListFilters, ArticlePag
@Injectable({ providedIn: 'root' })
export class ArticlesService {
private _apiVersion = '/v2';
private _apiDomain = '/cms';
constructor(
private apiService: ApiService
) { }
@@ -19,7 +19,7 @@ export class ArticlesService {
Object.assign(params, config.filters);
return this.apiService
.get(
`${this._apiVersion}/articles` + ((config.type === 'feed') ? '/feed' : ''),
`${this._apiDomain}/articles` + ((config.type === 'feed') ? '/feed' : ''),
new HttpParams({ fromObject: <{
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
}>params })
@@ -27,49 +27,49 @@ export class ArticlesService {
}
get(slug: string): Observable<ArticlePageData> {
/*return this.apiService.get(`${this._apiVersion}/articles/${slug}`).pipe(map(data => {
/*return this.apiService.get(`${this._apiDomain}/articles/${slug}`).pipe(map(data => {
data.article.tagList = data.article.tagNameTagTagLists;
delete data.article.tagNameTagTagLists;
return data;
}));*/
return this.apiService.get(`${this._apiVersion}/articles/${slug}`);
return this.apiService.get(`${this._apiDomain}/articles/${slug}`);
}
getAll(): Observable<Array<Article>> {
return this.apiService.get(`${this._apiVersion}/articles`).pipe(map(data => data.articles));
return this.apiService.get(`${this._apiDomain}/articles`).pipe(map(data => data.articles));
}
create(article: Article): Observable<Article> {
return this.apiService.post(`${this._apiVersion}/articles/`, { article: article }).pipe(map(data => data.article));
return this.apiService.post(`${this._apiDomain}/articles/`, { article: article }).pipe(map(data => data.article));
}
update(article: Article): Observable<Article> {
return this.apiService.put(`${this._apiVersion}/articles/${article.slug}`, { article: article }).pipe(map(data => data.article));
return this.apiService.put(`${this._apiDomain}/articles/${article.slug}`, { article: article }).pipe(map(data => data.article));
}
destroy(slug: string): Observable<boolean> {
return this.apiService.delete(`${this._apiVersion}/articles/${slug}`).pipe(map(data => data.deleted));
return this.apiService.delete(`${this._apiDomain}/articles/${slug}`).pipe(map(data => data.deleted));
}
save(article: Article): Observable<Article> {
// If we're updating an existing article
if (article.slug) {
return this.apiService.put(`${this._apiVersion}/articles/${article.slug}`, { article: article })
return this.apiService.put(`${this._apiDomain}/articles/${article.slug}`, { article: article })
.pipe(map(data => data.article));
// Otherwise, create a new article
} else {
return this.apiService.post(`${this._apiVersion}/articles/`, { article: article })
return this.apiService.post(`${this._apiDomain}/articles/`, { article: article })
.pipe(map(data => data.article));
}
}
favorite(slug: string): Observable<Article> {
return this.apiService.post(`${this._apiVersion}/articles/${slug}/favorite`);
return this.apiService.post(`${this._apiDomain}/articles/${slug}/favorite`);
}
unfavorite(slug: string): Observable<Article> {
return this.apiService.delete(`${this._apiVersion}/articles/${slug}/favorite`);
return this.apiService.delete(`${this._apiDomain}/articles/${slug}/favorite`);
}
}
@@ -14,7 +14,7 @@ import guildData from 'src/files-data/hw-guild-data.json'; // page Membres
@Injectable({ providedIn: 'root' })
export class HWClanService {
private apiVersion = '/v3';
private _apiDomain = '/herowars';
constructor(
private apiService: ApiService
) { }
@@ -30,7 +30,7 @@ export class HWClanService {
Object.assign(params, config.filters);
return this.apiService
.get(
`${this.apiVersion}/clans`,
`${this._apiDomain}/clans`,
new HttpParams({ fromObject: <{
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
}>params })
@@ -38,32 +38,32 @@ export class HWClanService {
}
get(id: string): Observable<HWClanPageData> {
return this.apiService.get(`${this.apiVersion}/clans/${id}`);
return this.apiService.get(`${this._apiDomain}/clans/${id}`);
}
getAll(): Observable<Array<HWClan>> {
return this.apiService.get(`${this.apiVersion}/clans`).pipe(map(data => data.clans));
return this.apiService.get(`${this._apiDomain}/clans`).pipe(map(data => data.clans));
}
create(clan: HWClan): Observable<HWClan> {
return this.apiService.post(`${this.apiVersion}/clans/`, { clan: clan }).pipe(map(data => data.clan));
return this.apiService.post(`${this._apiDomain}/clans/`, { clan: clan }).pipe(map(data => data.clan));
}
update(clan: HWClan): Observable<HWClan> {
return this.apiService.put(`${this.apiVersion}/clans/${clan.id}`, { clan: clan }).pipe(map(data => data.clan));
return this.apiService.put(`${this._apiDomain}/clans/${clan.id}`, { clan: clan }).pipe(map(data => data.clan));
}
destroy(id: string): Observable<boolean> {
return this.apiService.delete(`${this.apiVersion}/clans/${id}`).pipe(map(data => data.deleted));
return this.apiService.delete(`${this._apiDomain}/clans/${id}`).pipe(map(data => data.deleted));
}
save(clan: HWClan): Observable<HWClan> {
// If we're updating an existing clan
if (clan.id) {
return this.apiService.put(`${this.apiVersion}/clans/${clan.id}`, { clan: clan }).pipe(map(data => data.clan));
return this.apiService.put(`${this._apiDomain}/clans/${clan.id}`, { clan: clan }).pipe(map(data => data.clan));
// Otherwise, create a new clan
} else {
return this.apiService.post(`${this.apiVersion}/clans/`, { clan: clan }).pipe(map(data => data.clan));
return this.apiService.post(`${this._apiDomain}/clans/`, { clan: clan }).pipe(map(data => data.clan));
}
}
@@ -14,7 +14,7 @@ import guildData from 'src/files-data/hw-guild-data.json'; // page Membres
import guildStatistics from 'src/files-data/hw-guild-statistics.json'; // page Overview -> Statistics
@Injectable({ providedIn: 'root' })
export class HWMemberService {
private apiVersion = '/v3';
private _apiDomain = '/herowars';
constructor(
private _apiService: ApiService,
private _utilitiesService: UtilitiesService
@@ -26,7 +26,7 @@ export class HWMemberService {
Object.assign(params, config.filters);
return this._apiService
.get(
`${this.apiVersion}/members`,
`${this._apiDomain}/members`,
new HttpParams({ fromObject: <{
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
}>params })
@@ -34,32 +34,32 @@ export class HWMemberService {
}
get(id: string): Observable<HWMemberPageData> {
return this._apiService.get(`${this.apiVersion}/members/${id}`);
return this._apiService.get(`${this._apiDomain}/members/${id}`);
}
getAll(): Observable<Array<HWMember>> {
return this._apiService.get(`${this.apiVersion}/members`).pipe(map(data => data.members));
return this._apiService.get(`${this._apiDomain}/members`).pipe(map(data => data.members));
}
create(member: HWMember): Observable<HWMember> {
return this._apiService.post(`${this.apiVersion}/members/`, { member: member }).pipe(map(data => data.member));
return this._apiService.post(`${this._apiDomain}/members/`, { member: member }).pipe(map(data => data.member));
}
update(member: HWMember): Observable<HWMember> {
return this._apiService.put(`${this.apiVersion}/members/${member.id}`, { member: member }).pipe(map(data => data.member));
return this._apiService.put(`${this._apiDomain}/members/${member.id}`, { member: member }).pipe(map(data => data.member));
}
destroy(id: string): Observable<boolean> {
return this._apiService.delete(`${this.apiVersion}/members/${id}`).pipe(map(data => data.deleted));
return this._apiService.delete(`${this._apiDomain}/members/${id}`).pipe(map(data => data.deleted));
}
save(member: HWMember): Observable<HWMember> {
// If we're updating an existing member
if (member.id) {
return this._apiService.put(`${this.apiVersion}/members/${member.id}`, { member: member }).pipe(map(data => data.member));
return this._apiService.put(`${this._apiDomain}/members/${member.id}`, { member: member }).pipe(map(data => data.member));
// Otherwise, create a new member
} else {
return this._apiService.post(`${this.apiVersion}/members/`, { member: member }).pipe(map(data => data.member));
return this._apiService.post(`${this._apiDomain}/members/`, { member: member }).pipe(map(data => data.member));
}
}
+2 -2
View File
@@ -1,3 +1,3 @@
export * from './hwclan.service';
export * from './hwmember.service';
export * from './hw-clan.service';
export * from './hw-member.service';
+5 -5
View File
@@ -7,25 +7,25 @@ import { AeronefsPageData, CanopiesPageData, DropZonesPageData, JumpsPageData }
@Injectable({ providedIn: 'root' })
export class PagesService {
private apiVersion = '/v1';
private _apiDomain = '/skydive';
constructor(
private apiService: ApiService
) { }
getAeronefsPage(): Observable<AeronefsPageData> {
return this.apiService.get(`${this.apiVersion}/pages/aeronefs`).pipe(take(1));
return this.apiService.get(`${this._apiDomain}/pages/aeronefs`).pipe(take(1));
}
getCanopiesPage(): Observable<CanopiesPageData> {
return this.apiService.get(`${this.apiVersion}/pages/canopies`).pipe(take(1));
return this.apiService.get(`${this._apiDomain}/pages/canopies`).pipe(take(1));
}
getDropZonesPage(): Observable<DropZonesPageData> {
return this.apiService.get(`${this.apiVersion}/pages/dropzones`).pipe(take(1));
return this.apiService.get(`${this._apiDomain}/pages/dropzones`).pipe(take(1));
}
getJumpsPage(): Observable<JumpsPageData> {
return this.apiService.get(`${this.apiVersion}/pages/jumps`).pipe(take(1));
return this.apiService.get(`${this._apiDomain}/pages/jumps`).pipe(take(1));
}
}
+11 -11
View File
@@ -8,7 +8,7 @@ import { Product, ProductList, ProductListConfig, ProductListFilters, ProductPag
@Injectable({ providedIn: 'root' })
export class ProductsService {
private apiVersion = '/v2';
private _apiDomain = '/ecommerce';
constructor(
private apiService: ApiService
) { }
@@ -19,7 +19,7 @@ export class ProductsService {
Object.assign(params, config.filters);
return this.apiService
.get(
`${this.apiVersion}/products` + ((config.type === 'feed') ? '/feed' : ''),
`${this._apiDomain}/products` + ((config.type === 'feed') ? '/feed' : ''),
new HttpParams({ fromObject: <{
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
}>params })
@@ -27,41 +27,41 @@ export class ProductsService {
}
get(slug: string): Observable<ProductsPageData> {
return this.apiService.get(`${this.apiVersion}/product/${slug}`);
return this.apiService.get(`${this._apiDomain}/product/${slug}`);
}
getProductBySlug(slug: string): Observable<ProductPageData> {
return this.apiService.get(`${this.apiVersion}/product/${slug}`);
return this.apiService.get(`${this._apiDomain}/product/${slug}`);
}
getAll(): Observable<Array<Product>> {
return this.apiService.get(`${this.apiVersion}/products`).pipe(map(data => data.products));
return this.apiService.get(`${this._apiDomain}/products`).pipe(map(data => data.products));
}
getAllByCategory(category: string): Observable<ProductsPageData> {
return this.apiService.get(`${this.apiVersion}/products/${category}`); //.pipe(map(data => data.products));
return this.apiService.get(`${this._apiDomain}/products/${category}`); //.pipe(map(data => data.products));
}
create(product: Product): Observable<Product> {
return this.apiService.post(`${this.apiVersion}/products/`, { product: product }).pipe(map(data => data.product));
return this.apiService.post(`${this._apiDomain}/products/`, { product: product }).pipe(map(data => data.product));
}
update(product: Product): Observable<Product> {
return this.apiService.put(`${this.apiVersion}/products/${product.slug}`, { product: product }).pipe(map(data => data.product));
return this.apiService.put(`${this._apiDomain}/products/${product.slug}`, { product: product }).pipe(map(data => data.product));
}
destroy(slug: string): Observable<boolean> {
return this.apiService.delete(`${this.apiVersion}/products/${slug}`).pipe(map(data => data.deleted));
return this.apiService.delete(`${this._apiDomain}/products/${slug}`).pipe(map(data => data.deleted));
}
save(product: Product): Observable<Product> {
// If we're updating an existing product
if (product.slug) {
return this.apiService.put(`${this.apiVersion}/products/${product.slug}`, { product: product }).pipe(map(data => data.product));
return this.apiService.put(`${this._apiDomain}/products/${product.slug}`, { product: product }).pipe(map(data => data.product));
// Otherwise, create a new product
} else {
return this.apiService.post(`${this.apiVersion}/products/`, { product: product }).pipe(map(data => data.product));
return this.apiService.post(`${this._apiDomain}/products/`, { product: product }).pipe(map(data => data.product));
}
}
+4 -4
View File
@@ -7,22 +7,22 @@ import { map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class ProfilesService {
private _apiVersion = '/v2';
private _apiDomain = '/cms';
constructor(
private apiService: ApiService
) { }
get(username: string): Observable<Profile> {
return this.apiService.get(`${this._apiVersion}/profiles/${username}`)
return this.apiService.get(`${this._apiDomain}/profiles/${username}`)
.pipe(map((data: { profile: Profile }) => data.profile));
}
follow(username: string): Observable<Profile> {
return this.apiService.post(`${this._apiVersion}/profiles/${username}/follow`);
return this.apiService.post(`${this._apiDomain}/profiles/${username}/follow`);
}
unfollow(username: string): Observable<Profile> {
return this.apiService.delete(`${this._apiVersion}/profiles/${username}/follow`);
return this.apiService.delete(`${this._apiDomain}/profiles/${username}/follow`);
}
}
@@ -7,18 +7,18 @@ import { AeronefByImat, AeronefByYear } from '@models';
@Injectable({ providedIn: 'root' })
export class AeronefsService {
private _apiVersion = '/v1';
private _apiDomain = '/skydive';
constructor(
private apiService: ApiService
) { }
getAllByImat(): Observable<Array<AeronefByImat>> {
return this.apiService.get(`${this._apiVersion}/aeronefs/allByImat`)
return this.apiService.get(`${this._apiDomain}/aeronefs/allByImat`)
.pipe(map(data => data.aeronefs));
}
getAllByImatByYear(): Observable<Array<AeronefByYear>> {
return this.apiService.get(`${this._apiVersion}/aeronefs/allByImatByYear`)
return this.apiService.get(`${this._apiDomain}/aeronefs/allByImatByYear`)
.pipe(map(data => data.aeronefs));
}
@@ -7,28 +7,28 @@ import { CanopyBySize, CanopyByYear, CanopyModelBySize, CanopyModelByYear } from
@Injectable({ providedIn: 'root' })
export class CanopiesService {
private _apiVersion = '/v1';
private _apiDomain = '/skydive';
constructor(
private apiService: ApiService
) { }
getAllBySize(): Observable<Array<CanopyBySize>> {
return this.apiService.get(`${this._apiVersion}/canopies/allBySize`)
return this.apiService.get(`${this._apiDomain}/canopies/allBySize`)
.pipe(map(data => data.canopies));
}
getAllBySizeByYear(): Observable<Array<CanopyByYear>> {
return this.apiService.get(`${this._apiVersion}/canopies/allBySizeByYear`)
return this.apiService.get(`${this._apiDomain}/canopies/allBySizeByYear`)
.pipe(map(data => data.canopies));
}
getAllBySizeByModel(): Observable<Array<CanopyModelBySize>> {
return this.apiService.get(`${this._apiVersion}/canopies/allBySizeByModel`)
return this.apiService.get(`${this._apiDomain}/canopies/allBySizeByModel`)
.pipe(map(data => data.canopies));
}
getAllBySizeByModelByYear(): Observable<Array<CanopyModelByYear>> {
return this.apiService.get(`${this._apiVersion}/canopies/allBySizeByModelByYear`)
return this.apiService.get(`${this._apiDomain}/canopies/allBySizeByModelByYear`)
.pipe(map(data => data.canopies));
}
@@ -7,18 +7,18 @@ import { DropZoneByOaci, DropZoneByYear } from '@models';
@Injectable({ providedIn: 'root' })
export class DropZonesService {
private _apiVersion = '/v1';
private _apiDomain = '/skydive';
constructor(
private apiService: ApiService
) { }
getAllByOaci(): Observable<Array<DropZoneByOaci>> {
return this.apiService.get(`${this._apiVersion}/dropzones/allByOaci`)
return this.apiService.get(`${this._apiDomain}/dropzones/allByOaci`)
.pipe(map(data => data.dropzones));
}
getAllByOaciByYear(): Observable<Array<DropZoneByYear>> {
return this.apiService.get(`${this._apiVersion}/dropzones/allByOaciByYear`)
return this.apiService.get(`${this._apiDomain}/dropzones/allByOaciByYear`)
.pipe(map(data => data.dropzones));
}
+4 -4
View File
@@ -7,22 +7,22 @@ import { Qcm, QcmCategory, QcmQuestion } from '@models';
@Injectable({ providedIn: 'root' })
export class QcmService {
private _apiVersion = '/v1';
private _apiDomain = '/skydive';
constructor(
private apiService: ApiService
) { }
get(type: string): Observable<Qcm> {
return this.apiService.get(`${this._apiVersion}/qcm/type/${type}`).pipe(map(data => data.qcm));
return this.apiService.get(`${this._apiDomain}/qcm/type/${type}`).pipe(map(data => data.qcm));
}
saveChoices(question: object): Observable<QcmQuestion> {
return this.apiService.post(`${this._apiVersion}/qcm/choices`, { question: question })
return this.apiService.post(`${this._apiDomain}/qcm/choices`, { question: question })
.pipe(map(data => data.question));
}
saveQuestions(category: object): Observable<QcmCategory> {
return this.apiService.post(`${this._apiVersion}/qcm/questions`, { category: category })
return this.apiService.post(`${this._apiDomain}/qcm/questions`, { category: category })
.pipe(map(data => data.category));
}
}
+4 -4
View File
@@ -17,7 +17,7 @@ export class UserService implements OnDestroy {
public isAuthenticated = this.isAuthenticatedSubject.asObservable();
private apiVersion = '/v2';
private _apiDomain = '/cms';
constructor(
private readonly http: HttpClient,
private readonly jwtService: JwtService
@@ -32,7 +32,7 @@ export class UserService implements OnDestroy {
populate() {
// If JWT detected, attempt to get & store user's info
if (this.jwtService.getToken()) {
const user$: Observable<{ user: User }> = this.http.get<{ user: User }>(`${this.apiVersion}/user`).pipe(take(1));
const user$: Observable<{ user: User }> = this.http.get<{ user: User }>(`${this._apiDomain}/user`).pipe(take(1));
this._user = user$.subscribe({
next: (data) => this.setAuth(data.user),
error: () => this.purgeAuth()
@@ -64,7 +64,7 @@ export class UserService implements OnDestroy {
attemptAuth(type: string, credentials?: { email: string; password: string; }): Observable<{ user: User }> {
const route = (type === 'login') ? '/login' : '';
const user$: Observable<{ user: User }> = this.http.post<{ user: User }>(`${this.apiVersion}/user${route}`, { user: credentials });
const user$: Observable<{ user: User }> = this.http.post<{ user: User }>(`${this._apiDomain}/user${route}`, { user: credentials });
/*return user$.pipe(map(
(data: any) => {
this.setAuth(data.user);
@@ -84,7 +84,7 @@ export class UserService implements OnDestroy {
// Update the user on the server (email, pass, etc)
update(user: Partial<User>): Observable<{ user: User }> {
return this.http.put<{ user: User }>(`${this.apiVersion}/user`, { user }).pipe(
return this.http.put<{ user: User }>(`${this._apiDomain}/user`, { user }).pipe(
tap(({ user }) => {
this.currentUserSubject.next(user);
}),