commit 89fc29ab87fc8d94a38d4e9b225e7dcee19db0c8 Author: Hadi Khazaee Asl Date: Sun Aug 9 20:46:22 2026 +0330 Initial Commit on git.saherelm.ir ... diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7114b71 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc +# Only exists if Bazel was run +/bazel-out + +# dependencies +/node_modules + +# profiling files +chrome-profiler-events*.json + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db + +# +documentation +package-lock.json + +# +.angular \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..878cf7b --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# XFrameworkIdentitySdk + +provide OAuth Identity SDK of xFramework related to XIdentityServer project. + +## Maintainer + +Hadi Khazaee asl + +[https://www.saherelm.ir](https://www.saherelm.ir) + +[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com) diff --git a/angular.json b/angular.json new file mode 100644 index 0000000..fc73275 --- /dev/null +++ b/angular.json @@ -0,0 +1,45 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "cli": { + "analytics": false + }, + "version": 1, + "newProjectRoot": "projects", + "projects": { + "x-framework-identity-sdk": { + "projectType": "library", + "root": "projects/x-framework-identity-sdk", + "sourceRoot": "projects/x-framework-identity-sdk/src", + "prefix": "x", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "projects/x-framework-identity-sdk/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/x-framework-identity-sdk/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/x-framework-identity-sdk/tsconfig.lib.json" + }, + "ivy": { + "tsConfig": "projects/PROJECT_NAME/tsconfig.lib.ivy.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/x-framework-identity-sdk/src/test.ts", + "tsConfig": "projects/x-framework-identity-sdk/tsconfig.spec.json", + "karmaConfig": "projects/x-framework-identity-sdk/karma.conf.js" + } + } + } + } + }, + "defaultProject": "x-framework-identity-sdk" +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..cda5a11 --- /dev/null +++ b/package.json @@ -0,0 +1,77 @@ +{ + "name": "x-framework-identity-sdkholder", + "version": "0.0.2", + "description": "an isolation layoer of xFrameworkIdentitySDK module", + "keywords": [ + "x-framework", + "x-framework-identity-sdk" + ], + "homepage": "https://saherelm.ir", + "author": { + "name": "Hadi Khazaee Asl", + "email": "hadi_khazaee_asl@yahoo.com", + "url": "https://saherelm.ir" + }, + "scripts": { + "ng": "ng", + "publishModule": "npm run buildModule && npm run packModule", + "build:ivy": "ng build x-framework-identity-sdk --configuration=ivy", + "serveDocs": "compodoc -p projects/x-framework-identity-sdk/tsconfig.lib.json --theme material -s", + "buildModule": "npm run preBuildModule && ng build x-framework-identity-sdk --configuration production", + "packModule": "cd dist/x-framework-identity-sdk && npm pack && shx mv *.tgz ../../../../pack && cd ../..", + "prepareDependencies": "npm i --save-dev ../../pack/x-framework-core-0.0.1.tgz ../../pack/x-framework-services-0.0.1.tgz", + "prepareDependenciesByLink": "npm link x-framework-core x-framework-services", + "prepareDependenciesFromRegistry": "npm i -D --registry https://npm.farasoohub.ir x-framework-core x-framework-services", + "preBuildModule": "shx true --silent rm -rf dist && shx true --silent rm ../../pack/x-framework-identity-sdk*.tgz && npm run prepareDependencies", + "generateDocs": "compodoc -p projects/x-framework-identity-sdk/tsconfig.lib.json --theme material --hideGenerator --disableSourceCode --disableDomTree --disableTemplateTab --disableStyleTab --disablePrivate --disableLifeCycleHooks --disableRoutesGraph --disableCoverage --customFavicon ../xFrameworkDocuments/Resources/favicon.png --customLogo ../xFrameworkDocuments/Resources/favicon.png" + }, + "private": true, + "dependencies": { + "@angular/animations": "~12.0.4", + "@angular/common": "~12.0.4", + "@angular/compiler": "~12.0.4", + "@angular/core": "~12.0.4", + "@angular/forms": "~12.0.4", + "@angular/platform-browser": "~12.0.4", + "@angular/platform-browser-dynamic": "~12.0.4", + "@angular/router": "~12.0.4", + "rxjs": "~6.6.0", + "tslib": "^2.1.0", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~12.0.4", + "@angular/cdk": "^12.1.2", + "@angular/cli": "~12.0.4", + "@angular/compiler-cli": "~12.0.4", + "@angular/material": "^12.1.2", + "@angular/material-moment-adapter": "^12.1.2", + "@compodoc/compodoc": "^1.1.14", + "@ionic/angular": "^5.6.11", + "@types/crypto-js": "^4.0.2", + "@types/howler": "^2.2.3", + "@types/jasmine": "~3.6.0", + "@types/node": "^12.11.1", + "@types/uuid": "^8.3.1", + "crypto-js": "^4.0.0", + "howler": "^2.2.3", + "ionicons": "^5.5.2", + "jalali-moment": "^3.3.10", + "jasmine-core": "~3.7.0", + "karma": "~6.3.0", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage": "~2.0.3", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", + "libphonenumber-js": "^1.9.22", + "md5-typescript": "^1.0.5", + "moment": "^2.29.1", + "ng-packagr": "^12.0.0", + "ngx-device-detector": "^2.1.1", + "shx": "^0.3.3", + "typescript": "~4.2.3", + "uuid": "^8.3.2", + "x-framework-core": "file:../../pack/x-framework-core-0.0.1.tgz", + "x-framework-services": "file:../../pack/x-framework-services-0.0.1.tgz" + } +} diff --git a/projects/x-framework-identity-sdk/README.md b/projects/x-framework-identity-sdk/README.md new file mode 100644 index 0000000..0ca2058 --- /dev/null +++ b/projects/x-framework-identity-sdk/README.md @@ -0,0 +1,11 @@ +# XFrameworkIdentitySdk + +provide OAuth Identity SDK of xFramework related to XIdentityServer project. + +## Maintainer + +Hadi Khazaee asl + + + +hadi_khazaee_asl@yahoo.com diff --git a/projects/x-framework-identity-sdk/karma.conf.js b/projects/x-framework-identity-sdk/karma.conf.js new file mode 100644 index 0000000..3b7a99d --- /dev/null +++ b/projects/x-framework-identity-sdk/karma.conf.js @@ -0,0 +1,44 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, '../../coverage/x-framework-identity-sdk'), + subdir: '.', + reporters: [ + { type: 'html' }, + { type: 'text-summary' } + ] + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/projects/x-framework-identity-sdk/ng-package.json b/projects/x-framework-identity-sdk/ng-package.json new file mode 100644 index 0000000..18418e8 --- /dev/null +++ b/projects/x-framework-identity-sdk/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/x-framework-identity-sdk", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/projects/x-framework-identity-sdk/package.json b/projects/x-framework-identity-sdk/package.json new file mode 100644 index 0000000..cef18f1 --- /dev/null +++ b/projects/x-framework-identity-sdk/package.json @@ -0,0 +1,22 @@ +{ + "name": "x-framework-identity-sdk", + "version": "0.0.1", + "description": "provide OAuth Identity SDK of xFramework", + "keywords": [ + "x-framework", + "x-framework-identity-sdk" + ], + "homepage": "https://saherelm.ir", + "author": { + "name": "Hadi Khazaee Asl", + "email": "hadi_khazaee_asl@yahoo.com", + "url": "https://saherelm.ir" + }, + "peerDependencies": { + "@angular/common": "^12.0.4", + "@angular/core": "^12.0.4" + }, + "dependencies": { + "tslib": "^2.1.0" + } +} \ No newline at end of file diff --git a/projects/x-framework-identity-sdk/src/lib/base/x-base-api.service.ts b/projects/x-framework-identity-sdk/src/lib/base/x-base-api.service.ts new file mode 100644 index 0000000..e613886 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/base/x-base-api.service.ts @@ -0,0 +1,626 @@ +import { + XParam, + XLocale, + XHeader, + hasChild, + XKeyValue, + toXString, + XBaseService, + XContentType, + XExceptionIDs, + XOneOrManyType, + throwException, + isNullOrUndefined, + isNullOrEmptyString, +} from 'x-framework-core'; +import { Inject } from '@angular/core'; +import { + X_API_CONFIG, + X_FRAMEWORK_IDENTITY_SDK_CONFIG, +} from '../tokens/x-injectable-tokens'; +import { XQueryDto } from '../models/x-query.dto'; +import { setParams } from '../tools/x-endpoint.tools'; +import { XPageRequestDto } from '../models/x-page.dto'; +import { XEndPoints } from '../typings/x-endpoint.typings'; +import { XHttpUrlEncodingCodec } from '../providers/x-url.encoder'; +import { XApiConfiguration } from '../config/x-api-service.config'; +import { getAuthorizationHeaderValue } from '../constants/x-header.enum'; +import { HttpHeaders, HttpClient, HttpParams } from '@angular/common/http'; +import { XLoginResponseDto, XTokenResponseDto } from '../models/x-login.dto'; +import { XFrameworkIdentitySDKConfig } from '../config/x-framework-identity-sdk.config'; + +export abstract class XBaseApiService extends XBaseService { + // + public readonly basePath: string; + public readonly version: string; + public readonly baseEndPointRoute: string; + public readonly defaultHeaders = new HttpHeaders(); + public apiConfig = new XApiConfiguration(); + + constructor( + public readonly endPoint: XEndPoints, + public readonly applyApiIdentifier: boolean = false, + public readonly applyApiVersion: boolean = false, + protected httpClient: HttpClient, + @Inject(X_FRAMEWORK_IDENTITY_SDK_CONFIG) + config: XFrameworkIdentitySDKConfig, + @Inject(X_API_CONFIG) apiConfig: XApiConfiguration + ) { + // + super(config); + + // + if (apiConfig) { + // + this.version = ''; + if (applyApiVersion) { + this.version = isNullOrEmptyString(apiConfig.apiVersion) + ? null + : apiConfig.apiVersion.replace('/', ''); + } + + // + let bApiPath = apiConfig.baseApiPath || config.baseUrl; + if (isNullOrEmptyString(bApiPath)) { + throwException(XExceptionIDs.InvalidConfiguration); + } + + // + bApiPath = bApiPath.endsWith('/') + ? bApiPath.substring(0, bApiPath.length - 1) + : bApiPath; + + // + // Check Api Identifier ... + if (applyApiIdentifier) { + const apiIdentifier = config.apiIdentifier || apiConfig.apiIdentifier; + if (!isNullOrEmptyString(apiIdentifier)) { + bApiPath = `${bApiPath}/${apiIdentifier}`; + } + } + + // + this.basePath = bApiPath.endsWith('/') + ? bApiPath.substring(0, bApiPath.length - 1) + : bApiPath; + + // + this.apiConfig = apiConfig; + } + + // + this.baseEndPointRoute = isNullOrEmptyString(this.version) + ? `${this.basePath}/${this.endPoint}` + : `${this.basePath}/${this.version}/${this.endPoint}`; + + // + if ( + isNullOrUndefined(this.basePath) || + isNullOrUndefined(this.baseEndPointRoute) + ) { + throwException(XExceptionIDs.InvalidConfiguration); + } + + // + // Setting XPowered in Default Headers ... + this.defaultHeaders = this.defaultHeaders.set( + XHeader.XPoweredBy, + apiConfig.poweredByValue + ); + + // + // Setting Certificate Check ... + if ( + isNullOrUndefined(config.rejectUnauthorized) && + config.rejectUnauthorized === true + ) { + // + this.defaultHeaders = this.defaultHeaders.set( + XHeader.RejectUnauthorized, + toXString(config.rejectUnauthorized) + ); + } + } + + // + //#region Actions ... + + /** + * @param consumes string[] mime-types + * @return true: consumes contains 'multipart/form-data', false: otherwise + */ + public canConsumeForm(consumes: string[]): boolean { + // + const form = 'multipart/form-data'; + for (const consume of consumes) { + if (form === consume) { + return true; + } + } + + // + return false; + } + + /** + * adding Authentication & Authorization headers + */ + public addAuthentication( + headers: HttpHeaders, + tokens?: XTokenResponseDto + ): HttpHeaders; + public addAuthentication( + headers: HttpHeaders, + tokens?: XLoginResponseDto + ): HttpHeaders { + // + // if doesn't Provide Tokens, read and apply from Configurations ... + if (!tokens) { + // + // Handle Authentication (access_token) required + if ( + !isNullOrUndefined(this.apiConfig.apiKeys) && + !isNullOrEmptyString(this.apiConfig.apiKeys.Authorization) + ) { + headers = headers.set( + XHeader.Authorization, + this.apiConfig.apiKeys.Authorization + ); + } + } + + // + // if Provide Tokens read and apply from it ... + if (tokens) { + // + // Access Token ... + if (!isNullOrEmptyString(tokens.accessToken)) { + headers = headers.set( + XHeader.Authorization, + getAuthorizationHeaderValue(tokens.accessToken) + ); + } + + // + // Refresh Token ... + if (!isNullOrEmptyString(tokens.refreshToken)) { + headers = headers.set(XHeader.RefreshToken, tokens.refreshToken); + } + + // + // Expired At ... + if (!isNullOrUndefined(tokens.expiresAt)) { + headers = headers.set(XHeader.ExpiresAt, tokens.expiresAt.toString()); + } + } + + // + return headers; + } + + /** + * add custom header to request + * + * @param headers headers object + * @param key custom header key + * @param value custom header value + */ + public addCustomHeader( + headers?: HttpHeaders, + item?: XKeyValue, + forceAddDefaultHeaders = false + ): HttpHeaders { + // + // Instance Headers if not Exists ... + if (!headers) { + headers = new HttpHeaders(); + } + + // + // Add Default Headers if required ... + if ( + forceAddDefaultHeaders && + !isNullOrUndefined(this.defaultHeaders) && + hasChild(this.defaultHeaders.keys()) + ) { + // + this.defaultHeaders.keys().forEach((dKey) => { + // + const dValue = this.defaultHeaders.get(dKey); + headers = headers.set(dKey, dValue); + }); + } + + // + // Add Item ... + if ( + item && + !isNullOrUndefined(item) && + !isNullOrUndefined(item.key) && + !isNullOrUndefined(item.value) + ) { + // + headers = headers.set(item.key, toXString(item.value)); + } + + // + return headers; + } + + /** + * add default accept selected to headers + */ + public addAcceptJson(headers: HttpHeaders): HttpHeaders { + // + // to determine the Accept header + const httpHeaderAccepts: string[] = [XContentType.AppJson]; + const httpHeaderAcceptSelected: string | undefined = + this.apiConfig.selectHeaderAccept(httpHeaderAccepts); + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set(XHeader.Accept, httpHeaderAcceptSelected); + } + + // + return headers; + } + + /** + * adding application/octet-stream Accept in header ... + * + * @param headers the exists contents of header + */ + public addAcceptOctetStream(headers: HttpHeaders): HttpHeaders { + // + // to determine the Accept header + const httpHeaderAccepts: string[] = [XContentType.AppOctetStream]; + const httpHeaderAcceptSelected: string | undefined = + this.apiConfig.selectHeaderAccept(httpHeaderAccepts); + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set(XHeader.Accept, httpHeaderAcceptSelected); + } + + // + return headers; + } + + /** + * add language to headers ... + * + * @param headers the source headers ... + * @param language the specific language name ... + */ + public addLanguageHeader( + headers: HttpHeaders, + language: XLocale | string + ): HttpHeaders { + // + if (isNullOrEmptyString(language)) { + return headers; + } + + // + return this.addCustomHeader(headers, { + key: XParam.Language, + value: language, + }); + } + + /** + * add ContentType to headers + */ + public addContentType( + headers: HttpHeaders, + consumes?: string[] + ): HttpHeaders { + // + // to determine the Content-Type header + if (!consumes) { + consumes = []; + } + consumes = [...consumes, XContentType.AppJson]; + const httpContentTypeSelected: string | undefined = + this.apiConfig.selectHeaderContentType(consumes); + if (httpContentTypeSelected !== undefined) { + headers = headers.set(XHeader.ContentType, httpContentTypeSelected); + } + + // + return headers; + } + + /** + * generate proper http params based on XQuery ... + * + * @param query the Qury Options ... + */ + public generateXQueryHttpParams(query?: XQueryDto): HttpParams { + // + let result = new HttpParams({ + encoder: new XHttpUrlEncodingCodec(), + }); + + // + if (query) { + // + // Filter ... + if (!isNullOrEmptyString(query.filter)) { + result = result.set(XParam.Filter, query.filter as string); + } + + // + // ContainsDetail ... + if (!isNullOrUndefined(query.containsDetail)) { + result = result.set( + XParam.ContainsDetail, + toXString(query.containsDetail) + ); + } + + // + // EnableTracking ... + if (!isNullOrUndefined(query.enableTracking)) { + result = result.set( + XParam.EnableTracking, + toXString(query.enableTracking) + ); + } + + // + // SortBy ... + if (!isNullOrEmptyString(query.sortBy)) { + result = result.set(XParam.SortBy, toXString(query.sortBy)); + } + + // + // IsAscending ... + if (!isNullOrUndefined(query.isAscending)) { + result = result.set(XParam.IsAscending, toXString(query.isAscending)); + } + + // + // Page ... + if (!isNullOrUndefined(query.page)) { + result = result.set(XParam.Page, toXString(query.page)); + } + + // + // PageSize ... + if (!isNullOrUndefined(query.pageSize)) { + result = result.set(XParam.PageSize, toXString(query.pageSize)); + } + } + + // + return result; + } + + /** + * generate proper http params based on RequestPage ... + * + * @param request the RequestPage Options ... + */ + public generateXRequestPageHttpParams(request?: XPageRequestDto): HttpParams { + // + let queryParameters = new HttpParams({ + encoder: new XHttpUrlEncodingCodec(), + }); + + // + if (request) { + // + // First ... + if (!isNullOrUndefined(request.first)) { + queryParameters = queryParameters.set( + XParam.First, + toXString(request.first) + ); + } + + // + // Last ... + if (!isNullOrUndefined(request.last)) { + queryParameters = queryParameters.set( + XParam.Last, + toXString(request.last) + ); + } + + // + // After ... + if (!isNullOrEmptyString(request.after)) { + queryParameters = queryParameters.set( + XParam.After, + toXString(request.after) + ); + } + + // + // Before ... + if (!isNullOrEmptyString(request.before)) { + queryParameters = queryParameters.set( + XParam.Before, + toXString(request.before) + ); + } + + // + // SortBy ... + if (!isNullOrEmptyString(request.sortBy)) { + queryParameters = queryParameters.set( + XParam.SortBy, + toXString(request.sortBy) + ); + } + + // + // DescendingSort ... + if (!!request.descendingSort) { + queryParameters = queryParameters.set( + XParam.DescendingSort, + toXString(request.descendingSort) + ); + } + } + + // + return queryParameters; + } + + /** + * add specific param to an existings params collection ... + * + * @param params the exists params ... + * @param item the specific key value ... + */ + public addCustomParam( + params?: HttpParams, + item?: XKeyValue + ): HttpParams { + // + if (!params) { + // + params = new HttpParams({ + encoder: new XHttpUrlEncodingCodec(), + }); + } + + // + if ( + item && + !isNullOrUndefined(item) && + !isNullOrUndefined(item.key) && + !isNullOrUndefined(item.value) + ) { + // + params = params.set(item.key, toXString(item.value)); + } + + // + return params; + } + + /** + * add specific language param to an existings params collection ... + * + * @param params the exists params ... + * @param language the specific language value ... + */ + public addLanguageQueryParam( + params?: HttpParams, + language?: XLocale | string + ): HttpParams { + // + if (!params) { + // + params = new HttpParams({ + encoder: new XHttpUrlEncodingCodec(), + }); + } + + // + if (isNullOrEmptyString(language)) { + return params; + } + + // + params = params.set(XParam.Language, language); + + // + return params; + } + + /** + * attach containsDetail to Query String ... + * + * @param params exists param .. + * @param containsDetail the value ... + * @returns an instance of HttpParams ... + */ + public addContainsDetailQueryParam( + params?: HttpParams, + containsDetail?: boolean + ): HttpParams { + // + if (!params) { + // + params = new HttpParams({ + encoder: new XHttpUrlEncodingCodec(), + }); + } + + // + if (!!containsDetail) { + params = params.set(XParam.ContainsDetail, toXString(containsDetail)); + } + + // + return params; + } + + /** + * generate containsDetail Http Param ... + * + * @param containsDetail the param value ... + */ + public generateContainsDetailHttpParam(containsDetail: boolean): HttpParams { + // + let queryParameters = new HttpParams({ + encoder: new XHttpUrlEncodingCodec(), + }); + if (containsDetail) { + queryParameters = queryParameters.set( + XParam.ContainsDetail, + containsDetail.toString() + ); + } + + // + return queryParameters; + } + + /** + * generate and retrieve language http param ... + * + * @param language the specific language + */ + public generateLanguageHttpParam(language: XLocale | string): HttpParams { + // + let result = new HttpParams({ + encoder: new XHttpUrlEncodingCodec(), + }); + + // + if (!isNullOrEmptyString(language)) { + result = result.set(XParam.Language, language); + } + + // + return result; + } + + /** + * retrieve specific Action Route + * @param action the specified Action endpoint + * @returns complete action route contains route params + */ + public getActionRoute( + action: T, + params?: XOneOrManyType> + ) { + // + if (isNullOrEmptyString(String(action))) { + return this.baseEndPointRoute; + } + + // + let result = `${this.baseEndPointRoute}/${String(action)}`; + if (!hasChild(params)) { + return result; + } + + // + result = setParams(result, params); + return result; + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/config/x-api-service.config.ts b/projects/x-framework-identity-sdk/src/lib/config/x-api-service.config.ts new file mode 100644 index 0000000..6ae0146 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/config/x-api-service.config.ts @@ -0,0 +1,151 @@ +import { + XApiHeader, + getAuthorizationHeaderValue +} from '../constants/x-header.enum'; +import { isNullOrUndefined } from 'x-framework-core'; +import { XUserAccountInfo } from '../typings/x-account.typings'; + +export interface XApiConfigurationParameters { + apiKeys?: { [key: string]: string }; + username?: string; + password?: string; + accessToken?: string | (() => string); + baseApiPath?: string; + apiVersion?: string; + apiIdentifier?: string; + withCredentials?: boolean; + poweredByValue?: string; + revisionSecretKey?: string; +} + +export class XApiConfiguration { + apiKeys?: { [key: string]: string }; + username?: string; + password?: string; + accessToken?: string | (() => string); + baseApiPath?: string; + apiVersion?: string; + apiIdentifier?: string; + withCredentials?: boolean; + poweredByValue?: string; + revisionSecretKey?: string; + + constructor(configurationParameters: XApiConfigurationParameters = {}) { + this.apiKeys = configurationParameters.apiKeys; + this.username = configurationParameters.username; + this.password = configurationParameters.password; + this.accessToken = configurationParameters.accessToken; + this.baseApiPath = configurationParameters.baseApiPath; + this.apiVersion = configurationParameters.apiVersion; + this.apiIdentifier = configurationParameters.apiIdentifier; + this.withCredentials = configurationParameters.withCredentials; + this.poweredByValue = configurationParameters.poweredByValue; + this.revisionSecretKey = configurationParameters.revisionSecretKey; + } + + /** + * Select the correct content-type to use for a request. + * Uses {@link Configuration#isJsonMime} to determine the correct content-type. + * If no content type is found return the first found type if the contentTypes is not empty + * @param contentTypes - the array of content types that are available for selection + * @returns the selected content-type or undefined if no selection could be made. + */ + public selectHeaderContentType(contentTypes: string[]): string | undefined { + if (contentTypes.length === 0) { + return undefined; + } + + const type = contentTypes.find(x => this.isJsonMime(x)); + if (type === undefined) { + return contentTypes[0]; + } + return type; + } + + /** + * Select the correct accept content-type to use for a request. + * Uses {@link Configuration#isJsonMime} to determine the correct accept content-type. + * If no content type is found return the first found type if the contentTypes is not empty + * @param accepts - the array of content types that are available for selection. + * @returns the selected content-type or undefined if no selection could be made. + */ + public selectHeaderAccept(accepts: string[]): string | undefined { + if (accepts.length === 0) { + return undefined; + } + + const type = accepts.find(x => this.isJsonMime(x)); + if (type === undefined) { + return accepts[0]; + } + return type; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp( + '^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$', + 'i' + ); + return ( + mime != null && + (jsonMime.test(mime) || + mime.toLowerCase() === 'application/json-patch+json') + ); + } + + /** + * when a User LoggedIn, the User Info must passed to this object + * for using to add authentication in services ... + * + * @param userAccount loggedIn user info + */ + public async setUserAccount(userAccount: XUserAccountInfo): Promise { + // + // Fill Empty Values to user Account if it' null ... + // this is used for clearing stored data on logout event ... + if (isNullOrUndefined(userAccount)) { + userAccount = { + userSelectBy: '', + accessToken: '', + refreshToken: '', + expiresAt: -1, + profile: null + }; + } + + // + this.accessToken = userAccount.accessToken; + this.username = userAccount.userSelectBy; + + // + if (!this.apiKeys) { + this.apiKeys = {}; + } + + // + this.apiKeys[XApiHeader.ExpiresAt] = userAccount.expiresAt + ? userAccount.expiresAt.toString() + : '-1'; + this.apiKeys[XApiHeader.RefreshToken] = userAccount.refreshToken; + this.apiKeys[XApiHeader.Authorization] = getAuthorizationHeaderValue( + userAccount.accessToken + ); + } +} + +export const COLLECTION_FORMATS = { + csv: ',', + tsv: ' ', + ssv: ' ', + pipes: '|' +}; diff --git a/projects/x-framework-identity-sdk/src/lib/config/x-framework-identity-sdk.config.ts b/projects/x-framework-identity-sdk/src/lib/config/x-framework-identity-sdk.config.ts new file mode 100644 index 0000000..94516eb --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/config/x-framework-identity-sdk.config.ts @@ -0,0 +1,64 @@ +import { XFrameworkCoreConfig } from 'x-framework-core'; +import { XFrameworkServicesConfig } from 'x-framework-services'; + +export type XSharedConfig = XFrameworkCoreConfig & + XFrameworkServicesConfig & { [key: string]: any }; + +export interface XFrameworkIdentitySDKConfig extends XSharedConfig { + // + // Api Configuration ... + baseUrl: string; + apiVersion: string; + poweredValue: string; + apiIdentifier: string; + + // + // Server SSL Check ... + rejectUnauthorized?: boolean; + + // + // Registration Types ... + registrationType: string[]; + + // + // Registration Available ... + minAvailableDateConst: number; + maxAvailableDateConst: number; + + // + // Debouncing async actions Time ... + debounceTime: number; + + // + nameMinLength: number; + nameMaxLength: number; + + // + registrationOnlyWithInvitation: boolean; + + // + userNameMinLength: number; + userNameMaxLength: number; + + // + passwordPolicies?: { + requireDigit?: boolean; + requiredLength?: number; + requiredUniqueChars?: number; + requireLowercase?: boolean; + requireNonAlphanumeric?: boolean; + requireUppercase?: boolean; + }; + + // + passwordMinLength: number; + passwordMaxLength: number; + + // + verificationCodeLength: number; + delayBetweenTwoVerificationCode: number; + + // + // Attach Pages Index to Handle in Guard Rejects ... + Pages: any; +} diff --git a/projects/x-framework-identity-sdk/src/lib/constants/x-account.keys.ts b/projects/x-framework-identity-sdk/src/lib/constants/x-account.keys.ts new file mode 100644 index 0000000..d12a30a --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/constants/x-account.keys.ts @@ -0,0 +1,5 @@ +export enum XAccountStorageKeys { + UserSelectBy = '[ACCOUNT] USER', + DefaultUser = '[ACCOUNT] DEFAULT USER', + UserInfos = '[ACCOUNT] USER INFO' +} diff --git a/projects/x-framework-identity-sdk/src/lib/constants/x-api-scope.enum.ts b/projects/x-framework-identity-sdk/src/lib/constants/x-api-scope.enum.ts new file mode 100644 index 0000000..8fdd8d8 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/constants/x-api-scope.enum.ts @@ -0,0 +1,6 @@ +export enum XApiScope { + Read, + Write, + Admin, + Manage, +} diff --git a/projects/x-framework-identity-sdk/src/lib/constants/x-header.enum.ts b/projects/x-framework-identity-sdk/src/lib/constants/x-header.enum.ts new file mode 100644 index 0000000..3e3c2ba --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/constants/x-header.enum.ts @@ -0,0 +1,10 @@ +export enum XApiHeader { + ExpiresAt = 'expires_at', + RefreshToken = 'refresh_token', + Authorization = 'Authorization', + AuthorizationPrefix = 'Bearer ', +} + +export function getAuthorizationHeaderValue(token: string): string { + return `${XApiHeader.AuthorizationPrefix}${token}`; +} diff --git a/projects/x-framework-identity-sdk/src/lib/guards/auth.guard.ts b/projects/x-framework-identity-sdk/src/lib/guards/auth.guard.ts new file mode 100644 index 0000000..2af31d2 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/guards/auth.guard.ts @@ -0,0 +1,99 @@ +import { + Route, + CanLoad, + UrlTree, + UrlSegment, + CanActivate, + CanActivateChild, + RouterStateSnapshot, + ActivatedRouteSnapshot, +} from '@angular/router'; +import { Observable } from 'rxjs'; +import { map, tap, filter } from 'rxjs/operators'; +import { Injectable, Inject } from '@angular/core'; +import { XManagerService } from 'x-framework-services'; +import { XAccountService } from './../x-account.service'; +import { XLoggable, XExceptionIDs, isNullOrUndefined } from 'x-framework-core'; +import { X_FRAMEWORK_IDENTITY_SDK_CONFIG } from '../tokens/x-injectable-tokens'; +import { XFrameworkIdentitySDKConfig } from '../config/x-framework-identity-sdk.config'; + +@Injectable({ + providedIn: 'root', +}) +export class AuthGuard + extends XLoggable + implements CanActivate, CanActivateChild, CanLoad +{ + // + constructor( + private authService: XAccountService, + private managerService: XManagerService, + @Inject(X_FRAMEWORK_IDENTITY_SDK_CONFIG) + public config: XFrameworkIdentitySDKConfig + ) { + super(config); + } + + // + //#region Actions ... + // + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): + | Observable + | Promise + | boolean + | UrlTree { + return this.handleGuard(); + } + + // + canActivateChild( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): + | Observable + | Promise + | boolean + | UrlTree { + return this.handleGuard(); + } + + // + canLoad( + route: Route, + segments: UrlSegment[] + ): Observable | Promise | boolean { + return this.handleGuard(); + } + //#endregion + + // + //#region Private Action ... + private handleGuard(): Observable { + return this.authService.state$.pipe( + filter((state) => !isNullOrUndefined(state)), + map((state) => state.isLoggedIn), + tap((isPassed) => { + // + if (!isPassed) { + this.handleGuardReject(); + } + }) + ); + } + + // + private handleGuardReject(): void { + // + this.managerService.notificationService.presentErrorNotification({ + exception: XExceptionIDs.NotAuthorized, + dissmissable: true, + }); + + // + this.managerService.navigateByPageReplace(this.config.Pages.Login); + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/guards/not-auth.guard.ts b/projects/x-framework-identity-sdk/src/lib/guards/not-auth.guard.ts new file mode 100644 index 0000000..fb496e3 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/guards/not-auth.guard.ts @@ -0,0 +1,104 @@ +import { + Route, + UrlTree, + CanLoad, + UrlSegment, + CanActivate, + CanActivateChild, + RouterStateSnapshot, + ActivatedRouteSnapshot, +} from '@angular/router'; +import { Observable } from 'rxjs'; +import { map, tap, filter } from 'rxjs/operators'; +import { Injectable, Inject } from '@angular/core'; +import { XManagerService } from 'x-framework-services'; +import { XAccountService } from './../x-account.service'; +import { + XLoggable, + XExceptionIDs, + isNullOrUndefined, + toObservable, +} from 'x-framework-core'; +import { X_FRAMEWORK_IDENTITY_SDK_CONFIG } from '../tokens/x-injectable-tokens'; +import { XFrameworkIdentitySDKConfig } from '../config/x-framework-identity-sdk.config'; + +@Injectable({ + providedIn: 'root', +}) +export class NotAuthGuard + extends XLoggable + implements CanActivate, CanActivateChild, CanLoad +{ + // + constructor( + private authService: XAccountService, + private managerService: XManagerService, + @Inject(X_FRAMEWORK_IDENTITY_SDK_CONFIG) + public config: XFrameworkIdentitySDKConfig + ) { + super(config); + } + + // + //#region Actions ... + // + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): + | Observable + | Promise + | boolean + | UrlTree { + return this.handleGuard(); + } + + // + canActivateChild( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): + | Observable + | Promise + | boolean + | UrlTree { + // + return this.handleGuard(); + } + + // + canLoad( + route: Route, + segments: UrlSegment[] + ): Observable | Promise | boolean { + return this.handleGuard(); + } + //#endregion + + // + //#region Private Action ... + private handleGuard(): Observable { + return toObservable(this.authService.isLoggedIn()).pipe( + map((isLoggedIn) => !isLoggedIn), + tap((isPassed) => { + // + if (!isPassed) { + this.handleGuardReject(); + } + }) + ); + } + + // + private handleGuardReject(): void { + // + this.managerService.notificationService.presentErrorNotification({ + exception: XExceptionIDs.MustLogout, + dissmissable: true, + }); + + // + this.managerService.navigateByPageReplace(this.config.Pages.Startup); + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/helpers/x-account.helper.ts b/projects/x-framework-identity-sdk/src/lib/helpers/x-account.helper.ts new file mode 100644 index 0000000..4bb9345 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/helpers/x-account.helper.ts @@ -0,0 +1,13 @@ +import { XValidators } from 'x-framework-core'; +import { XUserAccountInfo } from '../typings/x-account.typings'; + +export function validateUserAccountInfo(model: XUserAccountInfo): void { + // + XValidators.validateNotNull(model); + XValidators.validateNotEmpty( + model.userSelectBy, + model.accessToken, + model.refreshToken + ); + XValidators.validateBiggerThan(0, model.expiresAt); +} diff --git a/projects/x-framework-identity-sdk/src/lib/models/x-device-info.dto.ts b/projects/x-framework-identity-sdk/src/lib/models/x-device-info.dto.ts new file mode 100644 index 0000000..d24a629 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/models/x-device-info.dto.ts @@ -0,0 +1,18 @@ +export interface XDeviceInfo { + os: string; + osVersion: string; + browser: string; + userAgent: string; + deviceType: XDeviceTypes; + userId?: number; + + identifier?: string; + token?: string; +} + +export enum XDeviceTypes { + Unknown, + Mobile, + Tablet, + Desktop +} diff --git a/projects/x-framework-identity-sdk/src/lib/models/x-discovery.dto.ts b/projects/x-framework-identity-sdk/src/lib/models/x-discovery.dto.ts new file mode 100644 index 0000000..21dd474 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/models/x-discovery.dto.ts @@ -0,0 +1,60 @@ +import { XBaseDto } from 'x-framework-core'; + +/** + * Models the response from an OpenID Connect discovery endpoint + */ +export interface XDiscoveryDto extends XBaseDto { + /** + * Gets the raw response. + */ + raw?: string; + /** + * Gets the response as a JObject. + */ + json?: any; + /** + * Gets a value indicating whether an error occurred. + */ + isError?: boolean; + /** + * Gets the status code. + */ + statusCode?: any; + /** + * Gets the error. + */ + error?: string; + /** + * Gets or sets the type of the error. + */ + errorType?: any; + /** + * Gets the exception. + */ + exception?: any; + /** + * Gets or sets the JSON web key set. + */ + keySet?: any; + issuer?: string; + authorizeEndpoint?: string; + tokenEndpoint?: string; + userInfoEndpoint?: string; + introspectionEndpoint?: string; + revocationEndpoint?: string; + deviceAuthorizationEndpoint?: string; + jwksUri?: string; + endSessionEndpoint?: string; + checkSessionIframe?: string; + registrationEndpoint?: string; + frontChannelLogoutSupported?: boolean; + frontChannelLogoutSessionSupported?: boolean; + grantTypesSupported?: Array; + codeChallengeMethodsSupported?: Array; + scopesSupported?: Array; + subjectTypesSupported?: Array; + responseModesSupported?: Array; + responseTypesSupported?: Array; + claimsSupported?: Array; + tokenEndpointAuthenticationMethodsSupported?: Array; +} diff --git a/projects/x-framework-identity-sdk/src/lib/models/x-friend-dto.ts b/projects/x-framework-identity-sdk/src/lib/models/x-friend-dto.ts new file mode 100644 index 0000000..7ffd5f8 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/models/x-friend-dto.ts @@ -0,0 +1,82 @@ +import { + XBaseDto, + isSameObject, + isNullOrUndefined, + isNullOrEmptyString, +} from 'x-framework-core'; +import { XFriendshipState } from './x-friendship.dto'; + +/** + * All Available Friendship Types ... + */ +export enum XFriendshipType { + Follower = 0, + Following = 1, +} + +/** + * a Dto interface Which Represent a User Friend ... + */ +export interface XFriendDto extends XBaseDto { + avatar: string; + userId: string; + username: string; + lastname: string; + firstname: string; + type?: XFriendshipType; + state?: XFriendshipState; +} + +export const defaultFriendDto: XFriendDto = { + avatar: '', + userId: '', + username: '', + lastname: '', + firstname: '', + type: undefined, + state: undefined, +}; + +export function prepareFriendDtoFields(model?: XFriendDto) { + // + if (isNullOrUndefined(model)) { + model = { + ...defaultFriendDto, + }; + } + + // + model = { + ...defaultFriendDto, + + // + avatar: !isNullOrEmptyString(model.avatar) + ? model.avatar + : defaultFriendDto.avatar, + userId: !isNullOrEmptyString(model.userId) + ? model.userId + : defaultFriendDto.userId, + username: !isNullOrEmptyString(model.username) + ? model.username + : defaultFriendDto.username, + lastname: !isNullOrEmptyString(model.lastname) + ? model.lastname + : defaultFriendDto.lastname, + firstname: !isNullOrEmptyString(model.firstname) + ? model.firstname + : defaultFriendDto.firstname, + + // + type: !isNullOrUndefined(model.type) ? model.type : defaultFriendDto.type, + state: !isNullOrUndefined(model.state) + ? model.state + : defaultFriendDto.state, + }; + + // + return model; +} + +export function isDefaultFriendDto(model: XFriendDto) { + return isSameObject(prepareFriendDtoFields(model), defaultFriendDto); +} diff --git a/projects/x-framework-identity-sdk/src/lib/models/x-friendship.dto.ts b/projects/x-framework-identity-sdk/src/lib/models/x-friendship.dto.ts new file mode 100644 index 0000000..ae3ab1a --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/models/x-friendship.dto.ts @@ -0,0 +1,33 @@ +import { XBaseDto } from 'x-framework-core'; + +export enum XFriendshipState { + None, + Pending, + Accepted, + Rejected, + Blocked +} + +export type XFriendshipStates = keyof typeof XFriendshipState; + +export interface XFriendshipFollowerDto extends XBaseDto { + state: XFriendshipState; + userId: string; + destId: string; +} + +export interface XFriendshipFollowingDto extends XBaseDto { + state: XFriendshipState; + userId: string; + destId: string; +} + +export interface XFriendshipInfoDto { + userId: string; + isFollower: boolean; + isFollowing: boolean; + followerState: XFriendshipState; + followingState: XFriendshipState; + followers: number; + followings: number; +} diff --git a/projects/x-framework-identity-sdk/src/lib/models/x-login.dto.ts b/projects/x-framework-identity-sdk/src/lib/models/x-login.dto.ts new file mode 100644 index 0000000..4528fd7 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/models/x-login.dto.ts @@ -0,0 +1,21 @@ +import { XLocale } from 'x-framework-core'; +import { XUserProfileDto } from './x-user.dto'; +import { XDeviceInfo } from './x-device-info.dto'; +import { XBaseResponseDto, XBaseRequestDto } from 'x-framework-core'; + +export interface XLoginRequestDto extends XBaseRequestDto { + userSelectBy: string; + password: string; + device?: XDeviceInfo; + language?: XLocale; +} + +export interface XTokenResponseDto extends XBaseResponseDto { + accessToken: string; + refreshToken: string; + expiresAt: number; +} + +export interface XLoginResponseDto extends XTokenResponseDto { + profile: XUserProfileDto; +} diff --git a/projects/x-framework-identity-sdk/src/lib/models/x-page.dto.ts b/projects/x-framework-identity-sdk/src/lib/models/x-page.dto.ts new file mode 100644 index 0000000..e10d9a0 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/models/x-page.dto.ts @@ -0,0 +1,15 @@ +export interface XPageRequestDto { + first?: number; + last?: number; + after: string; + before: string; + sortBy: string; + descendingSort: boolean; +} + +export interface XPageResponseDto { + nodes: Array; + totalCount: number; + hasNextPage: boolean; + hasPreviousPage: boolean; +} diff --git a/projects/x-framework-identity-sdk/src/lib/models/x-query.dto.ts b/projects/x-framework-identity-sdk/src/lib/models/x-query.dto.ts new file mode 100644 index 0000000..c854da0 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/models/x-query.dto.ts @@ -0,0 +1,19 @@ +export interface XQueryDto { + page?: number; + filter?: string; + sortBy?: string; + pageSize?: number; + isAscending?: boolean; + containsDetail?: boolean; + enableTracking?: boolean; +} + +export interface XQueryResultDto { + page?: number; + items?: Array; + pageSize?: number; + totalPages?: number; + totalItems?: number; + totalFilteredItems?: number; + totalFilteredPages?: number; +} diff --git a/projects/x-framework-identity-sdk/src/lib/models/x-registration-dto.ts b/projects/x-framework-identity-sdk/src/lib/models/x-registration-dto.ts new file mode 100644 index 0000000..0f58f35 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/models/x-registration-dto.ts @@ -0,0 +1,29 @@ +import { XDeviceInfo } from './x-device-info.dto'; +import { XBaseRequestDto, XBaseResponseDto } from 'x-framework-core'; + +export interface XActionRequestDto extends XBaseRequestDto { + acceptTerms?: boolean; + termsAndConditions?: string; + lang?: string; + actionToken?: string; + device?: XDeviceInfo; + firstName?: string; + lastName?: string; + dateOfBirth?: Date; + userId?: string; + userName?: string; + email?: string; + mobileNumber?: string; + password?: string; + newPassword?: string; + emailVerificationCode?: string; + mobileVerificationCode?: string; + thumbnail?: string; + returnUrl?: string; + avatar?: File; +} + +export interface XActionResponseDto extends XBaseResponseDto { + token?: string; + expiration?: Date; +} diff --git a/projects/x-framework-identity-sdk/src/lib/models/x-string.dto.ts b/projects/x-framework-identity-sdk/src/lib/models/x-string.dto.ts new file mode 100644 index 0000000..bc47603 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/models/x-string.dto.ts @@ -0,0 +1,60 @@ +import { + XLocale, + isSameObject, + isNullOrUndefined, + isNullOrEmptyString, +} from 'x-framework-core'; +import { XBaseNumberIDDto, XBaseRequestDto } from 'x-framework-core'; + +export interface XStringDto extends XBaseNumberIDDto { + language: XLocale | string; + resourceTitle: string; + translatedValue: string; +} + +export interface XUpdateStringsRequestDto extends XBaseRequestDto { + resources: Array; +} + +export const defaultStringDto: XStringDto = { + id: 0, + language: '', + resourceTitle: '', + translatedValue: '', +}; + +export function prepareStringDtoFields(model: XStringDto) { + // + if (!model) { + model = { + ...defaultStringDto, + }; + } + + // + if (isNullOrUndefined(model.id)) { + model.id = defaultStringDto.id; + } + + // + if (isNullOrEmptyString(model.language)) { + model.language = defaultStringDto.language; + } + + // + if (isNullOrEmptyString(model.resourceTitle)) { + model.resourceTitle = defaultStringDto.resourceTitle; + } + + // + if (isNullOrEmptyString(model.translatedValue)) { + model.translatedValue = defaultStringDto.translatedValue; + } + + // + return model; +} + +export function isDefaultStringDto(model: XStringDto) { + return isSameObject(prepareStringDtoFields(model), defaultStringDto); +} diff --git a/projects/x-framework-identity-sdk/src/lib/models/x-user.dto.ts b/projects/x-framework-identity-sdk/src/lib/models/x-user.dto.ts new file mode 100644 index 0000000..84c5099 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/models/x-user.dto.ts @@ -0,0 +1,189 @@ +import { + fromUtcDate, + XBaseRequestDto, + XBaseResponseDto, + XBaseNumberIDDto, +} from 'x-framework-core'; +import { isSameObject } from 'x-framework-core'; +import { XFriendshipInfoDto } from './x-friendship.dto'; + +export enum XGender { + Male, + Female, +} + +export interface XUserProfileDto extends XBaseResponseDto { + // + userId: string; + userName: string; + + email?: string; + emailConfirmed?: boolean; + phoneNumber?: string; + phoneNumberConfirmed?: boolean; + dateOfBirth?: Date; + + creationDate?: Date; + + firstName?: string; + lastName?: string; + lastLogin?: Date; + avatar?: string; + gender?: XGender; + friendshipInfo?: XFriendshipInfoDto; + roles?: Array; + avatars?: Array; + + isEnable?: boolean; + isBanned?: boolean; +} + +export const defaultUserProfileDto: XUserProfileDto = { + // + userId: '', + userName: '', + + // + email: '', + emailConfirmed: false, + + // + phoneNumber: '', + phoneNumberConfirmed: false, + + // + dateOfBirth: undefined, + creationDate: undefined, + + // + firstName: '', + lastName: '', + + // + lastLogin: undefined, + avatar: '', + + // + gender: XGender.Male, + + // + roles: [], + avatars: [], + + // + friendshipInfo: undefined, + + // + isEnable: false, + isBanned: false, +}; + +export function prepareUserProfileDtoFields( + model: XUserProfileDto +): XUserProfileDto { + // + if (!model) { + model = { + ...defaultUserProfileDto, + }; + } + + // + model = { + ...defaultUserProfileDto, + + // + userId: model.userId || defaultUserProfileDto.userId, + userName: model.userName || defaultUserProfileDto.userName, + + // + firstName: model.firstName || defaultUserProfileDto.firstName, + lastName: model.lastName || defaultUserProfileDto.lastName, + + // + gender: model.gender || defaultUserProfileDto.gender, + + // + email: model.email || defaultUserProfileDto.email, + emailConfirmed: + model.emailConfirmed || defaultUserProfileDto.emailConfirmed, + + // + phoneNumber: model.phoneNumber || defaultUserProfileDto.phoneNumber, + phoneNumberConfirmed: + model.phoneNumberConfirmed || defaultUserProfileDto.phoneNumberConfirmed, + + // + dateOfBirth: + fromUtcDate(model.dateOfBirth) || defaultUserProfileDto.dateOfBirth, + creationDate: + fromUtcDate(model.creationDate) || defaultUserProfileDto.creationDate, + lastLogin: fromUtcDate(model.lastLogin) || defaultUserProfileDto.lastLogin, + + // + avatar: model.avatar || defaultUserProfileDto.avatar, + friendshipInfo: + model.friendshipInfo || defaultUserProfileDto.friendshipInfo, + roles: model.roles || defaultUserProfileDto.roles, + avatars: model.avatars || defaultUserProfileDto.avatars, + + // + isEnable: model.isEnable || defaultUserProfileDto.isEnable, + isBanned: model.isBanned || defaultUserProfileDto.isBanned, + }; + + // + return model; +} + +export function isDefaultUserProfileDto(model: XUserProfileDto): boolean { + return isSameObject( + prepareUserProfileDtoFields(model), + defaultUserProfileDto + ); +} + +export interface XavatarDto extends XBaseResponseDto { + id: number; + name: string; + path: string; + thumb: string; + thumbPath: string; + creationDate: Date; +} + +export interface XUserNameIdResponseDto extends XBaseResponseDto { + id: string; + userName: string; +} + +export interface XUserNameIdRequestDto extends XBaseRequestDto { + ids: string[]; +} + +export interface XProfileUpdateRequestDto extends XBaseRequestDto { + email: string; + emailConfirmed?: boolean; + phoneNumber: string; + phoneNumberConfirmed?: boolean; + firstName: string; + lastName: string; + avatar: string; + dateOfBirth?: Date; + creationDate?: Date; + lastLogin?: Date; + gender: XGender; + roles?: Array; + isEnable?: boolean; + isBanned?: boolean; +} + +export interface XChangeRoleRequest extends XBaseRequestDto { + userId: string; + newRole: string; + bussinessRoleInfo: XBusinessOwner; +} + +export interface XBusinessOwner extends XBaseNumberIDDto { + userId: string; +} diff --git a/projects/x-framework-identity-sdk/src/lib/partial/x-account.admin.service.ts b/projects/x-framework-identity-sdk/src/lib/partial/x-account.admin.service.ts new file mode 100644 index 0000000..ca361ac --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/partial/x-account.admin.service.ts @@ -0,0 +1,175 @@ +import { Observable } from 'rxjs'; +import { + XAccountEndPoint, + XAccountEndPointParam, +} from '../typings/x-endpoint.typings'; +import { XUserNameIdRequestDto } from '../models/x-user.dto'; +import { HttpEvent, HttpResponse } from '@angular/common/http'; +import { XAccountRegistrationService } from './x-account.registration.service'; + +export class XAccountAdminService extends XAccountRegistrationService { + // + //#region Ban ... + /** + * ban users access to resources + * + * @param body the collection of user id's + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public ban( + body: XUserNameIdRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public ban( + body: XUserNameIdRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public ban( + body: XUserNameIdRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public ban( + body: XUserNameIdRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Ban); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region UnBan ... + /** + * unban users access to resources + * + * @param body the collection of user id's + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public unBan( + body: XUserNameIdRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public unBan( + body: XUserNameIdRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public unBan( + body: XUserNameIdRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public unBan( + body: XUserNameIdRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.UnBan); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region IsBanned ... + /** + * check a User is Banned or not + * + * @param destUser the user id's which going to check Banned or not + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public isBanned( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public isBanned( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public isBanned( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public isBanned( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.IsBanned, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // Prepare Result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/partial/x-account.authentication.service.ts b/projects/x-framework-identity-sdk/src/lib/partial/x-account.authentication.service.ts new file mode 100644 index 0000000..aa7b86b --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/partial/x-account.authentication.service.ts @@ -0,0 +1,542 @@ +import { + XHeader, + fromJson, + toPromise, + XValidators, + XExceptionIDs, + throwException, + isNullOrEmptyString, +} from 'x-framework-core'; +import { + XTokenResponseDto, + XLoginRequestDto, + XLoginResponseDto, +} from '../models/x-login.dto'; +import { Observable, of } from 'rxjs'; +import { + XAccountEndPoint, + XAccountEndPointParam, +} from '../typings/x-endpoint.typings'; +import { map, concatMap } from 'rxjs/operators'; +import { XDiscoveryDto } from '../models/x-discovery.dto'; +import { XApiScope } from '../constants/x-api-scope.enum'; +import { HttpResponse, HttpEvent } from '@angular/common/http'; +import { XAccountBaseService } from './x-account.base.service'; +import { XUserAccountInfo } from '../typings/x-account.typings'; + +export abstract class XAccountAuthenticationService extends XAccountBaseService { + // + //#region DiscoveryDocument ... + /** + * Retrieve Discovery Document + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public requestDiscoveryDocument( + observe?: 'body', + reportProgress?: boolean + ): Observable; + public requestDiscoveryDocument( + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public requestDiscoveryDocument( + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public requestDiscoveryDocument( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.DiscoveryDocument); + + // + // return result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region ScopeAccessToken ... + /** + * Request Specific Scope Access Token for API Actions + * + * @param scope the specific api scope + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public requestScopeAccessToken( + scope: XApiScope, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public requestScopeAccessToken( + scope: XApiScope, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public requestScopeAccessToken( + scope: XApiScope, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public requestScopeAccessToken( + scope: XApiScope, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addCustomHeader(headers, { + key: XHeader.Scope, + value: scope, + }); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RequestScopeAccessToken); + + // + // return result ... + return this.httpClient.post(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region Authenticate ... + /** + * Authenticate a User + * + * @param body an instance of XLoginRequest class which holds UserName and Password + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public authenticate( + body: XLoginRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public authenticate( + body: XLoginRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public authenticate( + body: XLoginRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public authenticate( + body: XLoginRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotNull(body); + XValidators.validateNotEmpty(body.password, body.userSelectBy); + + // + if (this.stateSnapshot && this.stateSnapshot.isLoggedIn) { + throwException(XExceptionIDs.MustLogout); + } + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Authenticate); + + // + // return result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region Login ... + /** + * Login a User + * + * @param body an instance of XLoginRequest class which holds UserName and Password + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public login( + body: XLoginRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public login( + body: XLoginRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public login( + body: XLoginRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public login( + body: XLoginRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotNull(body, body.device); + XValidators.validateNotEmpty( + body.password, + body.userSelectBy, + body.language + ); + + // + if (this.stateSnapshot && this.stateSnapshot.isLoggedIn) { + throwException(XExceptionIDs.MustLogout); + } + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Login); + + // + // return result ... + return this.httpClient + .post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }) + .pipe( + concatMap((res) => { + // + // Convert Login Response ... + const mResponse = fromJson(res); + + // + // Prepare XUserAccountInfo instance ... + const mUserInfo: XUserAccountInfo = { + ...mResponse, + userSelectBy: body.userSelectBy, + }; + + // + // Update User Account Info ... + return of( + this.addOrUpdateUserAccount(mUserInfo.userSelectBy, mUserInfo, true) + ).pipe(map((_) => mUserInfo)); + }), + // + // set User Account in API Config ... + concatMap((res) => { + return of(this.apiConfig.setUserAccount(res)).pipe(map((_) => res)); + }) + ); + } + //#endregion + + // + //#region RefreshTokens ... + /** + * Refresh Expired Tokens + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public refreshTokens( + observe?: 'body', + reportProgress?: boolean + ): Observable; + public refreshTokens( + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public refreshTokens( + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public refreshTokens( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + const tokens = this.retrieveTokens(); + headers = this.addAuthentication(headers, tokens); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RefreshTokens); + + // + // return result ... + return this.httpClient + .post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }) + .pipe( + concatMap((res) => { + // + // Convert Login Response ... + const mResponse = fromJson(res); + + // + // Prepare XUserAccountInfo instance ... + const mUserInfo: XUserAccountInfo = { + ...mResponse, + profile: this.stateSnapshot.profile, + userSelectBy: this.stateSnapshot.userSelectBy, + }; + + // + // Update User Account Info ... + return of( + this.addOrUpdateUserAccount(mUserInfo.userSelectBy, mUserInfo, true) + ).pipe( + map((_) => { + return { + userInfo: mUserInfo, + response: mResponse, + }; + }) + ); + }), + // + // set User Account in API Config ... + concatMap((res) => { + return of(this.apiConfig.setUserAccount(res.userInfo)).pipe( + map((_) => res.response) + ); + }) + ); + } + + /** + * Refresh Expired Tokens + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public refreshTokensOnly( + observe?: 'body', + reportProgress?: boolean + ): Observable; + public refreshTokensOnly( + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public refreshTokensOnly( + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public refreshTokensOnly( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + const tokens = this.retrieveTokens(); + headers = this.addAuthentication(headers, tokens); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RefreshTokens); + + // + // return result ... + return this.httpClient + .post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region Validate ... + /** + * Validate Revision Checksum + * + * @param revision token renew Revision + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public validateRevision( + revision: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public validateRevision( + revision: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public validateRevision( + revision: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public validateRevision( + revision: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + XValidators.validateNotEmpty(revision); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Validate); + + // + // return result ... + return this.httpClient.post( + endPointPath, + { revision }, + { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + } + ); + } + //#endregion + + // + //#region Logout ... + /** + * Logout User ... + */ + public async logout(returnUrl?: string) { + // + const isLoggedIn = await this.isLoggedIn(); + if (!isLoggedIn) { + throwException(XExceptionIDs.NotAllowed); + } + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Logout); + + // + // return result ... + try { + // + // Try Call Logout ... + await toPromise( + this.httpClient.post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + }) + ); + } catch {} + + // + const defUserId = await this.getDefaultUserIdentifier(); + XValidators.validateNotEmpty(defUserId); + + // + const defUser = await this.getDefaultUser(); + XValidators.validateNotNull(defUser); + + // + await this.removeDefaultUser(true); + + // + if (!isNullOrEmptyString(returnUrl)) { + this.router.navigateByUrl(returnUrl); + } + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/partial/x-account.base.service.ts b/projects/x-framework-identity-sdk/src/lib/partial/x-account.base.service.ts new file mode 100644 index 0000000..a983721 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/partial/x-account.base.service.ts @@ -0,0 +1,1241 @@ +import { + keys, + getKeys, + hasChild, + fromJson, + getArrayOf, + XValidators, + toObservable, + XExceptionIDs, + throwException, + toNormalString, + getErrorException, + isNullOrUndefined, + isNullOrEmptyString, +} from 'x-framework-core'; +import { + XAccountState, + XUserAccountInfo, + XUserAccountInfoIndex, +} from '../typings/x-account.typings'; +import { Inject } from '@angular/core'; +import { + X_API_CONFIG, + X_FRAMEWORK_IDENTITY_SDK_CONFIG, +} from '../tokens/x-injectable-tokens'; +import { + XAccountEndPoint, + XAccountEndPointParam, +} from './../typings/x-endpoint.typings'; +import { Router } from '@angular/router'; +import { HttpClient } from '@angular/common/http'; +import { BehaviorSubject, Observable, of } from 'rxjs'; +import { XManagerService } from 'x-framework-services'; +import { XUserProfileDto } from '../models/x-user.dto'; +import { map, concatMap, filter } from 'rxjs/operators'; +import { XBaseApiService } from '../base/x-base-api.service'; +import { XAccountStorageKeys } from '../constants/x-account.keys'; +import { XApiConfiguration } from '../config/x-api-service.config'; +import { validateUserAccountInfo } from '../helpers/x-account.helper'; +import { XLoginResponseDto, XTokenResponseDto } from '../models/x-login.dto'; +import { XFrameworkIdentitySDKConfig } from '../config/x-framework-identity-sdk.config'; + +export abstract class XAccountBaseService extends XBaseApiService { + // + stateSnapshot: XAccountState; + state$ = new BehaviorSubject(null); + + // + //#region Constructor ... + constructor( + protected router: Router, + protected httpClient: HttpClient, + @Inject(X_FRAMEWORK_IDENTITY_SDK_CONFIG) + config: XFrameworkIdentitySDKConfig, + @Inject(X_API_CONFIG) configuration: XApiConfiguration, + protected managerService: XManagerService + ) { + // + super('Account', false, false, httpClient, config, configuration); + + // + // handle initial state + toObservable(this.currentState()) + .pipe( + filter( + (state) => + !isNullOrUndefined(state) && + !isNullOrUndefined(state.profile) && + !isNullOrEmptyString(state.userSelectBy) && + !isNullOrEmptyString(state.accessToken) && + !isNullOrEmptyString(state.refreshToken) + ), + concatMap((state) => { + return toObservable(this.isExpired()).pipe( + map((isExpired) => { + return { + state, + isExpired, + }; + }) + ); + }), + concatMap((info) => { + if (!!info.isExpired) { + return toObservable(this.refreshCurrentToken()).pipe( + concatMap(() => { + return toObservable(this.currentState()).pipe( + map((newState) => { + return { + ...info, + state: { + ...newState, + }, + }; + }) + ); + }) + ); + } else { + return of(info); + } + }) + ) + .subscribe((info) => { + this.state$.next(info.state); + }); + } + //#endregion + + // + //#region Service Actions ... + /** + * + * @returns string message ... + */ + public hi(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Hi); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * + * @returns string message ... + */ + public passRequireXPowered(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.PassRequireXPowered); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * API Read Scope + * @returns string message ... + */ + public passReadAccess(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.PassReadAccess); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * API Write Scope + * @returns string message ... + */ + public passWriteAccess(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.PassWriteAccess); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * API Admin Scope + * @returns string message ... + */ + public passAdminAccess(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.PassAdminAccess); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * API Manage Scop + * @returns string message ... + */ + public passManageAccess(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.PassManageAccess); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * a simple Action which returns a List of Authenticated User Claims + * @returns string message which represent current user's claims ... + */ + public hiClaims(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.HiClaims); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * a simple Hello User for Checking Authentication and Policy + * @returns string message which contains authenticated user name ... + */ + public hiUser(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.HiUser); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * a simple Hello User for Checking Authentication and Policy + * @returns string message which contains authenticated user name ... + */ + public hiEnabledUser(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.HiEnabledUser); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * a simple Hello User for Checking Authentication and Policy + * @returns string message which contains authenticated user name ... + */ + public hiAgent(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.HiAgent); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * a simple Hello User for Checking Authentication and Policy + * @returns string message which contains authenticated user name ... + */ + public hiEnabledAgent(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.HiEnabledAgent); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * a simple Hello User for Checking Authentication and Policy + * @returns string message which contains authenticated user name ... + */ + public hiAdmin(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.HiAdmin); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + + /** + * a simple Hello User for Checking Authentication and Policy + * @returns string message which contains authenticated user name ... + */ + public hiEnabledAdmin(): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addAuthentication(headers); + + // + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.HiEnabledAdmin); + + // + // return result ... + return this.httpClient + .get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe: 'body', + reportProgress: false, + }) + .pipe(map((res) => fromJson(res))); + } + //#endregion + + // + //#region Account Handler ... + /** + * retrieve current user tokens ... + * + * @returns an instance of XTokenResponse + */ + public retrieveTokens(): XTokenResponseDto { + // + if (isNullOrUndefined(this.stateSnapshot)) { + return null; + } + + // + const result: XTokenResponseDto = { + accessToken: this.stateSnapshot.accessToken, + refreshToken: this.stateSnapshot.refreshToken, + expiresAt: this.stateSnapshot.expiresAt, + }; + + // + return result; + } + + /** + * reNew Account State ... + */ + public async reNewState(state?: XAccountState): Promise { + // + const currState = await this.currentState(); + this.state$.next({ + ...currState, + ...state, + }); + + // + this.apiConfig.setUserAccount(currState); + } + + /** + * retrieve current Account State ... + */ + public async currentState(): Promise { + // + let isLoggedIn = false; + let defaultUser = await this.getDefaultUser(); + if (!defaultUser) { + // + // Making Default State + defaultUser = { + userSelectBy: '', + accessToken: '', + refreshToken: '', + expiresAt: -1, + profile: null, + }; + } else { + isLoggedIn = true; + } + + // + const mCurrentState = { + isLoggedIn, + ...defaultUser, + } as XAccountState; + + // + this.stateSnapshot = mCurrentState; + this.apiConfig.setUserAccount(mCurrentState); + + // + return mCurrentState; + } + + /** + * Validate Token Responses with recieved revision ... + * + * @param model the model which going to validate + * @param revision the recieved revision + */ + public validateLoginResponseRevision( + model: XLoginResponseDto, + revision: string + ): boolean { + // + const source = + model.accessToken + + this.apiConfig.revisionSecretKey + + model.refreshToken + + this.apiConfig.revisionSecretKey + + model.expiresAt.toString(); + + // + return !isNullOrUndefined(model) && + !isNullOrEmptyString(revision) && + !isNullOrEmptyString(model.accessToken) && + !isNullOrEmptyString(model.refreshToken) && + model.expiresAt > -1 + ? this.managerService.securityService.toMd5(source) === revision + : false; + } + + // + //#region IsLoggedIn ... + public async isLoggedIn(): Promise { + // + const currState = await this.currentState(); + + // + return currState.isLoggedIn; + } + + public async isExpired(): Promise { + // + const defaultUser = await this.getDefaultUser(); + if (!defaultUser) { + throwException(XExceptionIDs.NotFound); + } + + // + let expiredAt = defaultUser.expiresAt; + const currentDate = new Date().getTime(); + + // + // Fix different length ... + const expiredAtStr = expiredAt.toString(); + const currentDateStr = currentDate.toString(); + const lengthDiff = currentDateStr.length - expiredAtStr.length; + if (lengthDiff > 0) { + expiredAt = expiredAt * Math.pow(10, lengthDiff); + } + + // + return currentDate >= expiredAt; + } + //#endregion + + // + //#region Refresh ... + /** + * refresh expired token ... + */ + public refreshCurrentToken() { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + const tokens = this.retrieveTokens(); + headers = this.addAuthentication(headers, tokens); + + // + const observe: any = 'body'; + const reportProgress: boolean = false; + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RefreshTokens); + + // + // return result ... + return this.httpClient + .post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }) + .pipe( + concatMap((res) => { + // + // Convert Login Response ... + const mResponse = fromJson(res); + + // + // Prepare XUserAccountInfo instance ... + const mUserInfo: XUserAccountInfo = { + ...mResponse, + profile: this.stateSnapshot.profile, + userSelectBy: this.stateSnapshot.userSelectBy, + }; + + // + // Update User Account Info ... + return of( + this.addOrUpdateUserAccount(mUserInfo.userSelectBy, mUserInfo, true) + ).pipe( + map((_) => { + return { + userInfo: mUserInfo, + response: mResponse, + }; + }) + ); + }), + // + // set User Account in API Config ... + concatMap((res) => { + return of(this.apiConfig.setUserAccount(res.userInfo)).pipe( + map((_) => res.response) + ); + }) + ); + } + //#endregion + + // + //#region Account Info Handlers ... + /** + * check a User Accounts Exists or not ... + * + * @param userSelectBy user identifier + */ + public async hasUserAccount(userSelectBy: string): Promise { + // + XValidators.validateNotEmpty(userSelectBy); + + // + userSelectBy = toNormalString(userSelectBy); + + // + const accounts = await this.getAccounts(); + if (!accounts) { + return false; + } + + // + const userIdentifiers = keys(accounts); + if (!hasChild(userIdentifiers)) { + return false; + } + + // + const result = + userIdentifiers.findIndex((i) => toNormalString(i) === userSelectBy) > -1; + + // + return result; + } + + /** + * retrieve specific user account + * + * @param userSelectBy user identifier + */ + public async getUserAccount(userSelectBy: string): Promise { + // + XValidators.validateNotEmpty(userSelectBy); + + // + userSelectBy = toNormalString(userSelectBy); + + // + const isExists = await this.hasUserAccount(userSelectBy); + if (!isExists) { + throwException(XExceptionIDs.NotFound); + } + + // + const accounts = await this.getAccountsAsArray(); + if (!hasChild(accounts)) { + throwException(XExceptionIDs.NotFound); + } + + // + const result = accounts.find( + (a) => toNormalString(a.userSelectBy) === userSelectBy + ); + + // + return result; + } + + /** + * Retrieve user info by it's token's ... + * + * @param info the user tokens info + */ + public async getUserByToken( + info: XLoginResponseDto + ): Promise { + // + const accounts = await this.getAccountsAsArray(); + if (!hasChild(accounts)) { + throwException(XExceptionIDs.NotFound); + } + + // + const account = accounts.find( + (a) => + a.accessToken === info.accessToken && + a.refreshToken === info.refreshToken && + a.expiresAt === info.expiresAt + ); + if (!accounts) { + throwException(XExceptionIDs.NotFound); + } + + // + return account; + } + + /** + * add new user account ... + * + * @param userSelectBy user identifier + * @param accountInfo user logged in account info + */ + public async addUserAccount( + userSelectBy: string, + accountInfo: XUserAccountInfo, + setAsDefault?: boolean + ): Promise { + // + XValidators.validateNotEmpty(userSelectBy); + validateUserAccountInfo(accountInfo); + + // + const isExists = await this.hasUserAccount(userSelectBy); + if (isExists) { + throwException(XExceptionIDs.Duplicate); + } + + // + const numOfAccounts = await this.countAccounts(); + if (numOfAccounts === 0) { + setAsDefault = true; + } + + // + const accounts = await this.getAccounts(); + accounts[userSelectBy] = accountInfo; + await this.updateAccounts(accounts); + + // + if (setAsDefault) { + await this.setDefaultUser(userSelectBy); + } + + // + await this.reNewState(); + } + + /** + * update exists user account info ... + * + * @param userSelectBy user identifier + * @param accountInfo user logged in account info + */ + public async updateUserAccount( + userSelectBy: string, + accountInfo: XUserAccountInfo, + setAsDefault?: boolean + ): Promise { + // + XValidators.validateNotEmpty(userSelectBy); + validateUserAccountInfo(accountInfo); + + // + const isExists = await this.hasUserAccount(userSelectBy); + if (!isExists) { + throwException(XExceptionIDs.NotFound); + } + + // + const accounts = await this.getAccounts(); + accounts[userSelectBy] = accountInfo; + await this.updateAccounts(accounts); + + // + if (setAsDefault) { + await this.setDefaultUser(userSelectBy); + } + + // + await this.reNewState(); + } + + /** + * Update an Existing Profile to User's Storage ... + * + * @param profile Updated Profile to Save ... + * @returns + */ + public async updateUserProfile(profile: XUserProfileDto): Promise { + // + XValidators.validateNotNull(profile); + + // + const defaultUser = await this.getDefaultUser(); + if ( + !defaultUser || + (defaultUser && !defaultUser.profile) || + (defaultUser && + defaultUser.profile && + defaultUser.profile.userId !== profile.userId) + ) { + return; + } + + // + defaultUser.profile = profile; + await this.addOrUpdateUserAccount(defaultUser.userSelectBy, defaultUser); + } + + /** + * add new/ update exists user account ... + * + * @param userSelectBy user identifier + * @param accountInfo user logged in account info + */ + public async addOrUpdateUserAccount( + userSelectBy: string, + accountInfo: XUserAccountInfo, + setAsDefault?: boolean + ): Promise { + // + XValidators.validateNotEmpty(userSelectBy); + validateUserAccountInfo(accountInfo); + + // + const isExists = await this.hasUserAccount(userSelectBy); + if (!isExists) { + await this.addUserAccount(userSelectBy, accountInfo, setAsDefault); + } else { + await this.updateUserAccount(userSelectBy, accountInfo, setAsDefault); + } + + // + await this.reNewState(); + } + + /** + * Update User's Refreshed Token ... + * + * @param oldInfo user's old token's info ... + * @param newInfo new user's token info which returned after token refreshing ... + */ + public async updateUserTokens( + oldInfo: XLoginResponseDto, + newInfo: XTokenResponseDto + ): Promise; + public async updateUserTokens( + oldInfo: XLoginResponseDto, + newInfo: XLoginResponseDto + ): Promise { + // + const count = await this.countAccounts(); + if (count === 0) { + throwException(XExceptionIDs.NotFound); + } + + // + const accounts = await this.getAccountsAsArray(); + + // + const account = accounts.find( + (a) => + a.accessToken === oldInfo.accessToken && + a.refreshToken === oldInfo.refreshToken && + a.expiresAt === oldInfo.expiresAt + ); + if (!account) { + throwException(XExceptionIDs.NotFound); + } + + // + account.accessToken = newInfo.accessToken; + account.refreshToken = newInfo.refreshToken; + account.expiresAt = newInfo.expiresAt; + + // + await this.updateUserAccount(account.userSelectBy, account); + await this.reNewState(); + } + + /** + * remove exists user account ... + * + * @param userSelectBy user identifier + */ + public async removeUserAccount(userSelectBy: string): Promise { + // + XValidators.validateNotEmpty(userSelectBy); + + // + userSelectBy = toNormalString(userSelectBy); + + // + const isExists = await this.hasUserAccount(userSelectBy); + if (!isExists) { + throwException(XExceptionIDs.NotFound); + } + + // + const accounts = await this.getAccountsAsArray(); + const updatedAccounts = accounts.filter( + (a) => toNormalString(a.userSelectBy) !== userSelectBy + ); + + // + const isDefaultUser = await this.isDefaultUser(userSelectBy); + if (isDefaultUser) { + await this.removeDefaultUser(); + await this.reNewState(); + } + + // + const updateAcountIndex: XUserAccountInfoIndex = {}; + updatedAccounts.forEach((a) => { + updateAcountIndex[a.userSelectBy] = a; + }); + + // + await this.updateAccounts(updateAcountIndex); + } + + /** + * Check a User Identifier is Defualt User or not + */ + public async isDefaultUser(userSelectBy: string): Promise { + // + XValidators.validateNotEmpty(userSelectBy); + + // + userSelectBy = toNormalString(userSelectBy); + + // + const defId = await this.getDefaultUserIdentifier(); + if (isNullOrEmptyString(defId)) { + return false; + } + + // + const result = toNormalString(defId) === userSelectBy; + + // + return result; + } + + /** + * read default user account identifier ... + */ + public async getDefaultUserIdentifier(): Promise { + // + const result = this.managerService.secureStorageService.readOfType( + XAccountStorageKeys.DefaultUser, + false + ); + + // + return result; + } + + /** + * retrieve default user account info + */ + public async getDefaultUser(): Promise { + // + const defaultUserIdentifier = await this.getDefaultUserIdentifier(); + if (isNullOrEmptyString(defaultUserIdentifier)) { + return null; + } + + // + const result = await this.getUserAccount(defaultUserIdentifier); + + // + return result; + } + + public async getDefaultUserTokens(): Promise { + // + const defaultUser = await this.getDefaultUser(); + if (!defaultUser) { + throw getErrorException(XExceptionIDs.NotFound); + } else { + // + const result: XLoginResponseDto = { + accessToken: defaultUser.accessToken, + refreshToken: defaultUser.refreshToken, + expiresAt: defaultUser.expiresAt, + profile: defaultUser.profile, + }; + + // + return result; + } + } + + /** + * set a user as default user ... + * + * @param userSelectBy user identifier + */ + public async setDefaultUser(userSelectBy: string): Promise { + // + XValidators.validateNotEmpty(userSelectBy); + + // + const isExists = await this.hasUserAccount(userSelectBy); + if (!isExists) { + throwException(XExceptionIDs.NotFound); + } + + // + this.managerService.secureStorageService.write( + XAccountStorageKeys.DefaultUser, + userSelectBy, + true + ); + + // + await this.reNewState(); + } + + /** + * Remove default user account + */ + public async removeDefaultUser(forceRemoveUser?: boolean): Promise { + // + const defId = await this.getDefaultUserIdentifier(); + if (isNullOrEmptyString(defId)) { + return; + } + + // + this.managerService.secureStorageService.remove( + XAccountStorageKeys.DefaultUser, + false + ); + + // + if (forceRemoveUser) { + const isExists = await this.hasUserAccount(defId); + if (isExists) { + await this.removeUserAccount(defId); + } + } + + // + const accounts = await this.getAccountsAsArray(); + if (hasChild(accounts)) { + await this.setDefaultUser(accounts[0].userSelectBy); + } + + // + await this.reNewState(); + } + + /** + * count all exists accounts + */ + public async countAccounts(): Promise { + // + const accounts = await this.getAccountsAsArray(); + + // + let result = 0; + if (!hasChild(accounts)) { + return result; + } + + // + result = accounts.length; + return result; + } + + /** + * retrieve all exists accounts + */ + public async getAccounts(): Promise { + // + const result = + this.managerService.secureStorageService.readOfType( + XAccountStorageKeys.UserInfos, + false + ) || {}; + + // + return result; + } + + /** + * update user Accounts ... + * + * @param model updated Accounts + */ + public async updateAccounts(model: XUserAccountInfoIndex): Promise { + // + XValidators.validateNotNull(model); + + // + await this.removeAccounts(); + + // + this.managerService.secureStorageService.write( + XAccountStorageKeys.UserInfos, + model, + true + ); + } + + /** + * get user accounts info as Array + */ + public async getAccountsAsArray(): Promise { + // + const accounts = await this.getAccounts(); + const result = getArrayOf(accounts) || []; + + // + return result; + } + + /** + * Remove All user Accounts + */ + public async removeAccounts(forceReNewState?: boolean): Promise { + // + this.managerService.secureStorageService.remove( + XAccountStorageKeys.UserInfos, + false + ); + + // + if (forceReNewState) { + await this.reNewState(); + } + } + + /** + * reset All Account Infos + */ + public async resetAccounts(): Promise { + // + const storageKeys = getKeys(XAccountStorageKeys); + + // + storageKeys.forEach((k) => { + this.managerService.secureStorageService.remove(k, false); + }); + + // + await this.reNewState(); + } + //#endregion + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/partial/x-account.final.service.ts b/projects/x-framework-identity-sdk/src/lib/partial/x-account.final.service.ts new file mode 100644 index 0000000..a0d2523 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/partial/x-account.final.service.ts @@ -0,0 +1,40 @@ +import { Observable, of } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; +import { isNullOrUndefined, notValue } from 'x-framework-core'; +import { XAccountSearchService } from './x-account.search.service'; + +export abstract class XAccountFinalService extends XAccountSearchService { + // + //#region Provider Actions ... + /** + * check a user id is same as current user id or not ... + * @param userId dest user id's + */ + isMe(userId?: string): Observable { + // + if (!userId) { + return of(false); + } + + // + return this.state$.pipe( + filter((res) => !isNullOrUndefined(res)), + map( + (state) => + state.profile.email === userId || + state.profile.userId === userId || + state.profile.userName === userId || + state.profile.phoneNumber === userId + ) + ); + } + + /** + * check a user id is not same as current user id or not ... + * @param userId dest user id's + */ + isNotMe(userId: string): Observable { + return notValue(this.isMe(userId)); + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/partial/x-account.friendship.service.ts b/projects/x-framework-identity-sdk/src/lib/partial/x-account.friendship.service.ts new file mode 100644 index 0000000..5c12c29 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/partial/x-account.friendship.service.ts @@ -0,0 +1,1350 @@ +import { Observable } from 'rxjs'; +import { + XFriendshipState, + XFriendshipInfoDto, + XFriendshipFollowerDto, + XFriendshipFollowingDto, +} from '../models/x-friendship.dto'; +import { + XAccountEndPoint, + XAccountEndPointParam, +} from '../typings/x-endpoint.typings'; +import { XFriendDto } from '../models/x-friend-dto'; +import { HttpEvent, HttpResponse } from '@angular/common/http'; +import { XQueryDto, XQueryResultDto } from '../models/x-query.dto'; +import { isNullOrEmptyString, XValidators } from 'x-framework-core'; +import { XAccountProfileService } from './x-account.profile.service'; + +export abstract class XAccountFriendshipService extends XAccountProfileService { + // + //#region Follow ... + /** + * Follow a User + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public follow( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public follow( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public follow( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public follow( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Follow, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // return response ... + return this.httpClient.post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region Cancel ... + /** + * Cancel Following + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public cancel( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public cancel( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public cancel( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public cancel( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Cancel, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // return response ... + return this.httpClient.post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region UnFollowFollower ... + /** + * Unfollow a Follower + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public unFollowFollower( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public unFollowFollower( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public unFollowFollower( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public unFollowFollower( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.UnFollowFollower, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // Result ... + return this.httpClient.post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region UnFollowFollowing ... + /** + * Unfollow Following + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public unFollowFollowing( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public unFollowFollowing( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public unFollowFollowing( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public unFollowFollowing( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.UnFollowFollowing, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // return result ... + return this.httpClient.post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region Block ... + /** + * Block a Follower + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public block( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public block( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public block( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public block( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Block, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // return response ... + return this.httpClient.post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region UnBlock ... + /** + * Unblock a Blocked User + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public unBlock( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public unBlock( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public unBlock( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public unBlock( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.UnBlock, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // Resut ... + return this.httpClient.post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region AcceptRequest ... + /** + * Accept a Following Request + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public accept( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public accept( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public accept( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public accept( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.AcceptRequest, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // return response ... + return this.httpClient.post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region RejectRequest ... + /** + * Reject a Following Request + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public reject( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public reject( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public reject( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public reject( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RejectRequest, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // Result ... + return this.httpClient.post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region FriendshipInfo ... + /** + * Get Friendship Info Model between Specific User and Current User + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public friendshipInfo( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public friendshipInfo( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public friendshipInfo( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public friendshipInfo( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.FriendshipInfo, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // Prepare Result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region IsFollower ... + /** + * Check a User IsFollower of Requested User + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public isFollower( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public isFollower( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public isFollower( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public isFollower( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.IsFollower, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // Return result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region FollowerState ... + /** + * Get Follower State of a User + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public followerState( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public followerState( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public followerState( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public followerState( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.FollowerState, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // return response ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region Follower ... + /** + * Get Specific Follower + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public follower( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public follower( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public follower( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public follower( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Follower, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // return response ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region Followers ... + /** + * Get Followers List Of Current User + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public followers( + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public followers( + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public followers( + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public followers( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Followers); + + // + // return result ... + return this.httpClient.get>(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region AllFollowers ... + /** + * Get All Followers List Includes Blocked, Requested and etc of Current User + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public allFollowers( + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public allFollowers( + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public allFollowers( + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public allFollowers( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.AllFollowers); + + // + // return response ... + return this.httpClient.get>(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region FollowersList ... + /** + * Return Followers UserName's List of Current User + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public followersList( + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public followersList( + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public followersList( + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public followersList( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.FollowersList); + + // + // return result ... + return this.httpClient.get>(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region QueryFollowers ... + /** + * query Followers + * + * @param query an instance of XQuery interface which provide projectio data + * @param destUser a User for retrieve it's Friend Info ... + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public queryFollowers( + query?: XQueryDto, + destUser?: string, + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public queryFollowers( + query?: XQueryDto, + destUser?: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public queryFollowers( + query?: XQueryDto, + destUser?: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public queryFollowers( + query?: XQueryDto, + destUser?: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + let params = this.generateXQueryHttpParams(query); + + // + // Attach DestUser to Query Params if Provided ... + if (!isNullOrEmptyString(destUser)) { + params = this.addCustomParam(params, { + key: 'destUser', + value: destUser, + }); + } + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.QueryFollowers); + + // + // Prepare Result ... + return this.httpClient.get>(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + params, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region IsFollowing ... + /** + * Check a User is in Followings of Current User + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public isFollowing( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public isFollowing( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public isFollowing( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public isFollowing( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.IsFollowing, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // Result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region FollowingState ... + /** + * Get Following State Relation between Specific User and Current User + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public followingState( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public followingState( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public followingState( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public followingState( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.FollowingState, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // return result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region Following ... + /** + * Get Specific Following Model + * + * @param destUser selected dest User + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public following( + destUser: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public following( + destUser: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public following( + destUser: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public following( + destUser: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotEmpty(destUser); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Following, { + key: XAccountEndPointParam.XUserSelectByParam, + value: destUser, + }); + + // + // return result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region Followings ... + /** + * Get Followings of Current User + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public followings( + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public followings( + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public followings( + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public followings( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Followings); + + // + // return result ... + return this.httpClient.get>(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region AllFollowings ... + /** + * Get All Following List Includes Blocked, Requested and etc of Current User + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public allFollowings( + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public allFollowings( + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public allFollowings( + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public allFollowings( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.AllFollowings); + + // + // return response ... + return this.httpClient.get>(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region FollowingsList ... + /** + * Return Following UserName's List of Current User + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public followingsList( + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public followingsList( + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public followingsList( + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public followingsList( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.FollowingsList); + + // + // return result ... + return this.httpClient.get>(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region QueryFollowings ... + /** + * query Followings + * + * @param query an instance of XQuery interface which provide projectio data + * @param destUser a User for retrieve it's Friend Info ... + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public queryFollowings( + query?: XQueryDto, + destUser?: string, + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public queryFollowings( + query?: XQueryDto, + destUser?: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public queryFollowings( + query?: XQueryDto, + destUser?: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public queryFollowings( + query?: XQueryDto, + destUser?: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + let params = this.generateXQueryHttpParams(query); + + // + // Attach DestUser to Query Params if Provided ... + if (!isNullOrEmptyString(destUser)) { + params = this.addCustomParam(params, { + key: 'destUser', + value: destUser, + }); + } + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.QueryFollowings); + + // + // Prepare Result ... + return this.httpClient.get>(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + params, + headers, + observe, + reportProgress, + }); + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/partial/x-account.profile.service.ts b/projects/x-framework-identity-sdk/src/lib/partial/x-account.profile.service.ts new file mode 100644 index 0000000..0193a22 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/partial/x-account.profile.service.ts @@ -0,0 +1,696 @@ +import { + XavatarDto, + XUserProfileDto, + XUserNameIdRequestDto, + XUserNameIdResponseDto, + XProfileUpdateRequestDto, +} from '../models/x-user.dto'; +import { Observable } from 'rxjs'; +import { + XAccountEndPoint, + XAccountEndPointParam, +} from '../typings/x-endpoint.typings'; +import { XHeader, XValidators } from 'x-framework-core'; +import { HttpResponse, HttpEvent } from '@angular/common/http'; +import { XQueryDto, XQueryResultDto } from '../models/x-query.dto'; +import { XAccountAuthenticationService } from './x-account.authentication.service'; + +export abstract class XAccountProfileService extends XAccountAuthenticationService { + // + //#region GetNames ... + /** + * Get UserName's Related to a Collection of user identifiers + * + * @param ids selected user identifiers + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getNames( + ids: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public getNames( + ids: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public getNames( + ids: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public getNames( + ids: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotEmpty(ids); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.GetNames, { + key: XAccountEndPointParam.XIds, + value: ids, + }); + + // + // Prepare Result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region GetNameIds ... + /** + * Get UserName's Related to a Collection of user id's + * + * @param model selected user identifiers + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getNameIds( + model: XUserNameIdRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public getNameIds( + model: XUserNameIdRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public getNameIds( + model: XUserNameIdRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public getNameIds( + model: XUserNameIdRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(model); + XValidators.validateHasChilds(model.ids); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.GetNameIds); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, model, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region Profile ... + /** + * Get Profile Object of Specific User + * + * @param userSelectBy selected user identifier + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getProfile( + userSelectBy: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public getProfile( + userSelectByParam: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public getProfile( + userSelectByParam: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public getProfile( + userSelectByParam: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotEmpty(userSelectByParam); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.Profile, { + key: XAccountEndPointParam.XUserSelectByParam, + value: userSelectByParam, + }); + + // + // Prepare Result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region QueryProfiles ... + /** + * query user profiles + * + * @param query an instance of XQuery interface which provide projectio data + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public queryProfiles( + query?: XQueryDto, + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public queryProfiles( + query?: XQueryDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public queryProfiles( + query?: XQueryDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public queryProfiles( + query?: XQueryDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + const queryParameters = this.generateXQueryHttpParams(query); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.QueryProfiles); + + // + // Prepare Result ... + return this.httpClient.get>(endPointPath, { + params: queryParameters, + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region QueryAvatars ... + /** + * query specific users avatars ... + * + * @param userSelectByParam user identifier + * @param query an instance of XQuery interface which provide projectio data + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public queryAvatars( + userSelectByParam?: string, + query?: XQueryDto, + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public queryAvatars( + userSelectByParam?: string, + query?: XQueryDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public queryAvatars( + userSelectByParam?: string, + query?: XQueryDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public queryAvatars( + userSelectByParam?: string, + query?: XQueryDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + const queryParameters = this.generateXQueryHttpParams(query); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.QueryAvatars, { + key: XAccountEndPointParam.XUserSelectByParam, + value: userSelectByParam, + }); + + // + // Prepare Result ... + return this.httpClient.get>(endPointPath, { + params: queryParameters, + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region ProfileUpdate ... + /** + * Update Specific User's Profile + * + * @param userSelectByParam selected user identifier + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public updateProfile( + userSelectByParam: string, + body: XProfileUpdateRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public updateProfile( + userSelectByParam: string, + body: XProfileUpdateRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public updateProfile( + userSelectByParam: string, + body: XProfileUpdateRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public updateProfile( + userSelectByParam: string, + body: XProfileUpdateRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotEmpty(userSelectByParam); + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.ProfileUpdate, { + key: XAccountEndPointParam.XUserSelectByParam, + value: userSelectByParam, + }); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region FullProfileUpdate ... + /** + * Update Specific User's Profile + * + * @param userSelectByParam selected user identifier + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public fullUpdateProfile( + userSelectByParam: string, + body: XProfileUpdateRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public fullUpdateProfile( + userSelectByParam: string, + body: XProfileUpdateRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public fullUpdateProfile( + userSelectByParam: string, + body: XProfileUpdateRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public fullUpdateProfile( + userSelectByParam: string, + body: XProfileUpdateRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotEmpty(userSelectByParam); + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.FullProfileUpdate, { + key: XAccountEndPointParam.XUserSelectByParam, + value: userSelectByParam, + }); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region AddAvatar ... + /** + * Add a New Profile Image + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public addAvatar( + body: File, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public addAvatar( + body: File, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public addAvatar( + body: File, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public addAvatar( + body: File, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + const formData: FormData = new FormData(); + formData.append(XHeader.File, body, body.name); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.AddAvatar); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, formData, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + + //#endregion + + // + //#region AddAvatars ... + /** + * Add a Collection of Profile Images + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public addAvatars( + body: File[], + observe?: 'body', + reportProgress?: boolean + ): Observable; + public addAvatars( + body: File[], + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public addAvatars( + body: File[], + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public addAvatars( + body: File[], + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateHasChilds(body); + + // + const formData: FormData = new FormData(); + body.forEach((f) => { + formData.append(XHeader.Files, f, f.name); + }); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.AddAvatars); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, formData, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region SetAvatar ... + /** + * set Specified Profile Image as Avatar + * + * @param id profile image id + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public setAvatar( + id: number, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public setAvatar( + id: number, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public setAvatar( + id: number, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public setAvatar( + id: number, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotNull(id); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.SetAvatar, { + key: XAccountEndPointParam.XId, + value: id.toString(), + }); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, null, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region RemoveAvatars ... + /** + * Remove Profile Images + * + * @param ids profile image id's which need's to remove + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public removeAvatars( + ids: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public removeAvatars( + ids: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public removeAvatars( + ids: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public removeAvatars( + ids: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotEmpty(ids); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RemoveAvatars, { + key: XAccountEndPointParam.XIds, + value: ids, + }); + + // + // Prepare Result ... + return this.httpClient.delete(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/partial/x-account.registration.service.ts b/projects/x-framework-identity-sdk/src/lib/partial/x-account.registration.service.ts new file mode 100644 index 0000000..c0b7a7f --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/partial/x-account.registration.service.ts @@ -0,0 +1,1121 @@ +import { of } from 'rxjs'; +import { Observable } from 'rxjs'; +import { + XActionRequestDto, + XActionResponseDto, +} from '../models/x-registration-dto'; +import { + XAccountEndPoint, + XAccountEndPointParam, +} from '../typings/x-endpoint.typings'; +import { HttpEvent, HttpResponse } from '@angular/common/http'; +import { XHeader, XCountryCode, XValidators } from 'x-framework-core'; +import { XAccountFriendshipService } from './x-account.friendship.service'; + +export abstract class XAccountRegistrationService extends XAccountFriendshipService { + // + //#region IsConfirmedEmail ... + /** + * Check User is Confirmed Email or not + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public isConfirmedEmail( + observe?: 'body', + reportProgress?: boolean + ): Observable; + public isConfirmedEmail( + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public isConfirmedEmail( + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public isConfirmedEmail( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.IsConfirmedEmail); + + // + // return result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region IsConfirmedMobile ... + /** + * Check User is Confirmed Mobile or not + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public isConfirmedMobile( + observe?: 'body', + reportProgress?: boolean + ): Observable; + public isConfirmedMobile( + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public isConfirmedMobile( + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public isConfirmedMobile( + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.IsConfirmedMobile); + + // + // return result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region CanRegisterUserName ... + /** + * Check a UserName is Available For Registration or not + * + * @param userName an string value which represent desired username + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public canRegisterUserName( + userName?: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public canRegisterUserName( + userName?: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public canRegisterUserName( + userName?: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public canRegisterUserName( + userName?: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + try { + XValidators.validateNotEmpty(userName); + } catch { + return of(false); + } + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.CanRegisterUserName, { + key: XAccountEndPointParam.XUserName, + value: userName, + }); + + // + // return result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region CanRegisterMobileNumber ... + /** + * Check a Mobile Number is Available For Registration or not + * + * @param mobileNumber an string value which represent desired mobile number + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public canRegisterMobileNumber( + mobileNumber?: string, + defaultCountryCode?: XCountryCode, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public canRegisterMobileNumber( + mobileNumber?: string, + defaultCountryCode?: XCountryCode, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public canRegisterMobileNumber( + mobileNumber?: string, + defaultCountryCode?: XCountryCode, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public canRegisterMobileNumber( + mobileNumber?: string, + defaultCountryCode?: XCountryCode, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + try { + XValidators.validateNotEmpty(mobileNumber); + XValidators.validatePhoneNumber( + mobileNumber, + defaultCountryCode || this.config.defaultCountryCode + ); + } catch { + return of(false); + } + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.CanRegisterMobileNumber, { + key: XAccountEndPointParam.XMobileNumber, + value: mobileNumber, + }); + + // + // return result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region CanRegisterEmail ... + /** + * Check a Email Address is Available For Registration or not + * + * @param email an string value which represent desired email address + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public canRegisterEmail( + email?: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public canRegisterEmail( + email?: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public canRegisterEmail( + email?: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public canRegisterEmail( + email?: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + try { + XValidators.validateNotEmpty(email); + XValidators.validateEmailAddress(email); + } catch { + return of(false); + } + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.CanRegisterEmail, { + key: XAccountEndPointParam.XEmail, + value: email, + }); + + // + // return result ... + return this.httpClient.get(endPointPath, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region RequestConfirmEmail ... + /** + * Request For Email Address Change/ Confirm + * + * @param body request for action + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public requestConfirmEmail( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public requestConfirmEmail( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public requestConfirmEmail( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public requestConfirmEmail( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RequestConfirmEmail); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region RequestConfirmMobile ... + /** + * Request For Mobile Number Change/ Confirm + * + * @param body request for action + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public requestConfirmMobile( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public requestConfirmMobile( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public requestConfirmMobile( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public requestConfirmMobile( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RequestConfirmMobile); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region RequestResetPassword ... + /** + * Request Reset Password Action + * + * @param body request for action + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public requestResetPassword( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public requestResetPassword( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public requestResetPassword( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public requestResetPassword( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RequestResetPassword); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region RequestConfirmRegistration ... + /** + * Request Registration Confirm + * + * @param body request for action + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public requestConfirmRegistration( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public requestConfirmRegistration( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public requestConfirmRegistration( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public requestConfirmRegistration( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RequestConfirmRegistration); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region ConfirmRegistration ... + /** + * Confirm Registration ... + * + * @param body request for action + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public confirmRegistration( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public confirmRegistration( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public confirmRegistration( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public confirmRegistration( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.ConfirmRegistration); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region ConfirmEmailAddress ... + /** + * Confirm Email Address + * + * @param body request for action + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public confirmEmail( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public confirmEmail( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public confirmEmail( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public confirmEmail( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.ConfirmEmailAddress); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region ConfirmMobileNumber ... + /** + * Confirm Mobile Number + * + * @param body request for action + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public confirmMobile( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public confirmMobile( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public confirmMobile( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public confirmMobile( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.ConfirmMobileNumber); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region InviteUser ... + /** + * Invite a User to Register on Dashboard + * + * @param body action request object + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public invite( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public invite( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public invite( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public invite( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.InviteUser); + + // + // return result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region RequestRegistration ... + /** + * Recieve some Basic Informations and Start Registration Proccess if they Valid Registration Proccess Starts with Invoking this Action + * + * @param body action request object + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public requestRegistration( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public requestRegistration( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public requestRegistration( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public requestRegistration( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.RequestRegistration); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region AddAccountInfo ... + /** + * Add User Account Informations + * + * @param body action request object + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public addAccountInfo( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public addAccountInfo( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public addAccountInfo( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public addAccountInfo( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.AddAccountInfo); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region AttachProfileImage ... + /** + * Upload and Attach a Profile Image for User + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public attachProfileImage( + body: File, + actionToken: string, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public attachProfileImage( + body: File, + actionToken: string, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public attachProfileImage( + body: File, + actionToken: string, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public attachProfileImage( + body: File, + actionToken: string, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + XValidators.validateNotEmpty(actionToken); + + // + const formData: FormData = new FormData(); + formData.append(XHeader.File, body, body.name); + formData.append(XHeader.ActionToken, actionToken); + + // + // Instantiiate Headers from Default Headers ... + const headers = this.defaultHeaders; + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.AttachProfileImage); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, formData, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region FinishRegistration ... + /** + * Finish Registrationn Proccess + * + * @param body action request object + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public finishRegistration( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public finishRegistration( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public finishRegistration( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public finishRegistration( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.FinishRegistration); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region ChangePassword ... + /** + * Change a User's Password + * + * @param body an instance of XActionRequestDto class which provider requirement for action + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public changePassword( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public changePassword( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public changePassword( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public changePassword( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate Args ... + XValidators.validateNotNull(body, body.device); + XValidators.validateNotEmpty( + body.lang, + body.password, + body.newPassword, + body.returnUrl + ); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + headers = this.addAuthentication(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.ChangePassword); + + // + // return result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion + + // + //#region ResetPassword ... + /** + * Reset Password + * + * @param body request for action + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public resetPassword( + body: XActionRequestDto, + observe?: 'body', + reportProgress?: boolean + ): Observable; + public resetPassword( + body: XActionRequestDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>; + public resetPassword( + body: XActionRequestDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>; + public resetPassword( + body: XActionRequestDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + // Validate ARgs ... + XValidators.validateNotNull(body); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.ResetPassword); + + // + // Prepare Result ... + return this.httpClient.post(endPointPath, body, { + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/partial/x-account.search.service.ts b/projects/x-framework-identity-sdk/src/lib/partial/x-account.search.service.ts new file mode 100644 index 0000000..d99e1d8 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/partial/x-account.search.service.ts @@ -0,0 +1,69 @@ +import { Observable } from 'rxjs'; +import { + XAccountEndPoint, + XAccountEndPointParam, +} from '../typings/x-endpoint.typings'; +import { XUserProfileDto } from '../models/x-user.dto'; +import { HttpEvent, HttpResponse } from '@angular/common/http'; +import { XAccountAdminService } from './x-account.admin.service'; +import { XQueryDto, XQueryResultDto } from '../models/x-query.dto'; + +export abstract class XAccountSearchService extends XAccountAdminService { + // + //#region QueryOpenToSearchProfiles ... + /** + * query user profiles + * + * @param query an instance of XQuery interface which provide projectio data + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public queryOpenToSearchProfiles( + query?: XQueryDto, + observe?: 'body', + reportProgress?: boolean + ): Observable>; + public queryOpenToSearchProfiles( + query?: XQueryDto, + observe?: 'response', + reportProgress?: boolean + ): Observable>>; + public queryOpenToSearchProfiles( + query?: XQueryDto, + observe?: 'events', + reportProgress?: boolean + ): Observable>>; + public queryOpenToSearchProfiles( + query?: XQueryDto, + observe: any = 'body', + reportProgress: boolean = false + ): Observable { + // + const queryParameters = this.generateXQueryHttpParams(query); + + // + // Instantiiate Headers from Default Headers ... + let headers = this.defaultHeaders; + headers = this.addAuthentication(headers); + headers = this.addAcceptJson(headers); + headers = this.addContentType(headers); + + // + // Prepare Endpoint + const endPointPath = this.getActionRoute< + XAccountEndPoint, + XAccountEndPointParam + >(XAccountEndPoint.QueryOpenToSearchProfiles); + + // + // Prepare Result ... + return this.httpClient.get>(endPointPath, { + params: queryParameters, + withCredentials: this.apiConfig.withCredentials, + headers, + observe, + reportProgress, + }); + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/providers/x-url.encoder.ts b/projects/x-framework-identity-sdk/src/lib/providers/x-url.encoder.ts new file mode 100644 index 0000000..aa9cfd3 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/providers/x-url.encoder.ts @@ -0,0 +1,12 @@ +import { HttpUrlEncodingCodec } from '@angular/common/http'; + +export class XHttpUrlEncodingCodec extends HttpUrlEncodingCodec { + encodeKey(k: string): string { + k = super.encodeKey(k); + return k.replace(/\+/gi, '%2B'); + } + encodeValue(v: string): string { + v = super.encodeValue(v); + return v.replace(/\+/gi, '%2B'); + } +} diff --git a/projects/x-framework-identity-sdk/src/lib/services/api-http-interceptor.service.ts b/projects/x-framework-identity-sdk/src/lib/services/api-http-interceptor.service.ts new file mode 100644 index 0000000..a56b895 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/services/api-http-interceptor.service.ts @@ -0,0 +1,343 @@ +import { + XHeader, + isObject, + countChild, + findAllIndex, + toObservable, + XBaseService, + XExceptionIDs, + getErrorException, + XStringPlaceHolder, + isNullOrEmptyString, +} from 'x-framework-core'; +import { + HttpEvent, + HttpRequest, + HttpHandler, + HttpEventType, + HttpInterceptor, + HttpErrorResponse, +} from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { Injectable, Inject } from '@angular/core'; +import { XManagerService } from 'x-framework-services'; +import { XAccountService } from './../x-account.service'; +import { XLoginResponseDto } from '../models/x-login.dto'; +import { finalize, catchError, concatMap, map } from 'rxjs/operators'; +import { X_FRAMEWORK_IDENTITY_SDK_CONFIG } from '../tokens/x-injectable-tokens'; +import { XFrameworkIdentitySDKConfig } from '../config/x-framework-identity-sdk.config'; + +@Injectable({ + providedIn: 'root', +}) +export class ApiHttpInterceptorService + extends XBaseService + implements HttpInterceptor +{ + // + private isRefreshing = false; + + // + constructor( + private authService: XAccountService, + @Inject(X_FRAMEWORK_IDENTITY_SDK_CONFIG) + config: XFrameworkIdentitySDKConfig, + private managerService: XManagerService + ) { + super(config); + } + + intercept( + req: HttpRequest, + next: HttpHandler + ): Observable> { + return this.handleInterceptRequest(req, next); + } + + handleInterceptRequest( + req: HttpRequest, + next: HttpHandler + ): Observable> { + // + // Adding Identifier ... + req = this.applyHeadersToRequest(req); + + // + // Apply RefreshToken Handler ... + const handledRefreshToken = !this.isRefreshing + ? this.applyRefreshTokenHandler(next.handle(req), req, next) + : next.handle(req); + // const handledRefreshToken = next.handle(req); + + // + // Apply Response Headers Handler ... + const handledResponseHeaders = + this.applyResponseHandler(handledRefreshToken); + + // + // Apply Error Handler ... + const handledErrors = this.applyErrorHandler(handledResponseHeaders); + + // + // Apply Finally Handler ... + const handledFinally = this.applyFinally(handledErrors); + + // + // Final Level ... + const result = handledFinally; + + // + return result; + } + + // + //#region Actions / Handlers ... + applyHeadersToRequest(request: HttpRequest): HttpRequest { + return request.clone({ + setHeaders: { + XRegisteredTo: `${this.config.poweredValue}`, + }, + }); + } + + applyFinally( + current: Observable> + ): Observable> { + // + return current.pipe( + finalize(() => { + // + // Collection of instructions after finish or error ... + this.isRefreshing = false; + this.managerService.loading = false; + }) + ); + } + + applyRefreshTokenHandler( + current: Observable>, + req: HttpRequest, + next: HttpHandler + ): Observable> { + // + return current.pipe( + catchError((error) => { + // + if (error instanceof HttpErrorResponse && error.status === 401) { + this.logError('401 => ', error); + + // + const isExpired$ = toObservable(this.authService.isExpired()); + + // + return isExpired$.pipe( + concatMap((isExpired) => { + if (isExpired) { + return this.handleRefreshToken(req, next); + } else { + return throwError(error); + } + }) + ); + } else { + return throwError(error); + } + }) + ); + } + + applyResponseHandler( + current: Observable> + ): Observable> { + // + return current.pipe( + map((event: HttpEvent) => { + // this.handleResponseHeaders(event); + return event; + }) + ); + } + + applyErrorHandler( + current: Observable> + ): Observable> { + return current.pipe( + catchError((error: HttpErrorResponse) => { + // + this.managerService.loading = false; + const mException = getErrorException(error.error); + + // + // Check mException contains string formatter place holder or not ... + if (mException.message.includes(XStringPlaceHolder)) { + // + // try to retrieve place holder contents ... + const placeHolderIndexes = findAllIndex( + mException.message, + XStringPlaceHolder + ); + const countOfPlaceHolders = countChild(placeHolderIndexes); + + // + // retrieve place holder content from message ... + const placeHolderContents: string[] = []; + let placeHolderContent = + error.error && isObject(error.error) + ? (error.error.message as string) + : ''; + if ( + !isNullOrEmptyString(placeHolderContent) && + placeHolderContent.includes(':') + ) { + // + placeHolderContent = placeHolderContent + .substring(placeHolderContent.indexOf(':') + 1) + .trim(); + + // + if (countOfPlaceHolders > 1 && placeHolderContent.includes(',')) { + placeHolderContents.push(...placeHolderContent.split(',')); + } else { + placeHolderContents.push(placeHolderContent); + } + } + + // + // now we can fix and add place holder params to message of mException using stringFormatter function ... + mException.payload = [...placeHolderContents]; + } + + // + this.managerService.error = mException; + + // + this.logError('HttpInterceptor: ', error, mException); + + // + return throwError(mException); + }) + ); + } + + handleRefreshToken( + req: HttpRequest, + next: HttpHandler + ): Observable> { + // + if (!this.isRefreshing) { + this.isRefreshing = true; + + // + return this.authService.state$.pipe( + concatMap((accountState) => { + return this.authService.refreshTokens().pipe( + map((loginResponse) => { + return { + accountState, + loginResponse, + }; + }) + ); + }), + concatMap((accountInfo) => { + // + this.logWarn('Login Refresh Response: ', accountInfo.loginResponse); + + // + let headers = this.authService.defaultHeaders; + headers = this.authService.addAuthentication( + headers, + accountInfo.loginResponse + ); + headers = Object.assign(req.headers, headers); + + // + req = req.clone({ + headers, + }); + + // + // Update User Account ... + const updateAccount$ = toObservable( + this.authService.updateUserTokens( + accountInfo.accountState, + accountInfo.loginResponse + ) + ); + + // + return updateAccount$.pipe( + concatMap(() => { + return next + .handle(req) + .pipe(finalize(() => (this.isRefreshing = false))); + }) + ); + }) + ); + } else { + return this.handleRefreshToken(req, next); + } + } + + async handleResponseHeaders(event: HttpEvent): Promise { + // + if (event.type === HttpEventType.Response) { + // + const headers = event.headers; + + // + const isContainsNewAuthentication = + headers.has(XHeader.Authorization) && + headers.has(XHeader.RefreshToken) && + headers.has(XHeader.ExpiresAt) && + headers.has(XHeader.RevisionChecksum); + + // + if (isContainsNewAuthentication) { + // + const oldTokens = await this.authService.getDefaultUserTokens(); + + // + const accessToken = headers + .get(XHeader.Authorization) + .replace(XHeader.AuthorizationPrefix, ''); + const refreshToken = headers.get(XHeader.RefreshToken); + const expiresAt = +headers.get(XHeader.ExpiresAt); + const revisionChecksum = headers.get(XHeader.RevisionChecksum); + + // + const profile = oldTokens.profile; + + // + const newTokens: XLoginResponseDto = { + accessToken, + refreshToken, + expiresAt, + profile, + }; + + // + const isValid = this.authService.validateLoginResponseRevision( + newTokens, + revisionChecksum + ); + + // + if (isValid) { + await this.authService.updateUserTokens(oldTokens, newTokens); + } else { + // + this.managerService.notificationService.presentErrorNotification({ + exception: XExceptionIDs.InvalidToken, + dissmissable: true, + }); + + // + await this.authService.logout(); + } + } + } + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/tokens/x-injectable-tokens.ts b/projects/x-framework-identity-sdk/src/lib/tokens/x-injectable-tokens.ts new file mode 100644 index 0000000..2fe6c01 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/tokens/x-injectable-tokens.ts @@ -0,0 +1,11 @@ +import { InjectionToken } from '@angular/core'; +import { XApiConfiguration } from '../config/x-api-service.config'; +import { XFrameworkIdentitySDKConfig } from '../config/x-framework-identity-sdk.config'; + +export const X_FRAMEWORK_IDENTITY_SDK_CONFIG = new InjectionToken( + 'x_config' +); + +export const X_API_CONFIG = new InjectionToken( + 'apiConfiguration' +); diff --git a/projects/x-framework-identity-sdk/src/lib/tools/x-endpoint.tools.ts b/projects/x-framework-identity-sdk/src/lib/tools/x-endpoint.tools.ts new file mode 100644 index 0000000..2d54930 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/tools/x-endpoint.tools.ts @@ -0,0 +1,70 @@ +import { + values, + toArray, + hasChild, + XKeyValue, + XOneOrManyType, + isNullOrEmptyString, +} from 'x-framework-core'; +import { XAccountEndPointParam } from '../typings/x-endpoint.typings'; + +export function isContainsParams( + route: string, + params?: XOneOrManyType> +) { + // + // Validate Args ... + if (isNullOrEmptyString(route)) { + return false; + } + + // + const paramValues = values(XAccountEndPointParam) as Array; + if (!hasChild(paramValues)) { + return false; + } + + // + let result = paramValues.some((param) => route.includes(param)); + if (!result) { + // + const paramsArr = toArray(params).map((p) => p.key) as Array; + if (!hasChild(paramsArr)) { + return false; + } + + // + result = paramsArr.some((param) => route.includes(param)); + } + + // + return result; +} + +export function setParams( + route: string, + params?: XOneOrManyType> +) { + // + // Validate Args ... + if ( + !hasChild(params) || + !isContainsParams(route, params) || + isNullOrEmptyString(route) + ) { + return route; + } + + // + // Do Actions ... + toArray(params).forEach((paramKeyValue) => { + // + route = route.replace( + String(paramKeyValue.key), + encodeURIComponent(String(paramKeyValue.value)) + ); + }); + + // + return route; +} diff --git a/projects/x-framework-identity-sdk/src/lib/typings/x-account.typings.ts b/projects/x-framework-identity-sdk/src/lib/typings/x-account.typings.ts new file mode 100644 index 0000000..0a78327 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/typings/x-account.typings.ts @@ -0,0 +1,13 @@ +import { XLoginResponseDto } from '../models/x-login.dto'; + +export interface XUserAccountInfo extends XLoginResponseDto { + userSelectBy: string; +} + +export interface XUserAccountInfoIndex { + [userSelectBy: string]: XUserAccountInfo; +} + +export interface XAccountState extends XUserAccountInfo { + isLoggedIn: boolean; +} diff --git a/projects/x-framework-identity-sdk/src/lib/typings/x-endpoint.typings.ts b/projects/x-framework-identity-sdk/src/lib/typings/x-endpoint.typings.ts new file mode 100644 index 0000000..4d22e35 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/typings/x-endpoint.typings.ts @@ -0,0 +1,130 @@ +export enum XEndPoint { + Account, +} + +export type XEndPoints = keyof typeof XEndPoint | string; + +/** + * prepare all Route Params which used in Account Services Endpoints ... + */ +export enum XAccountEndPointParam { + XId = '{xId}', + XIds = '{xIds}', + XEmail = '{xEmail}', + XUserName = '{xUserName}', + XMobileNumber = '{xMobileNumber}', + XUserSelectByParam = '{xUserSelectByParam}', +} + +export type XAccountEndPointParams = keyof typeof XAccountEndPointParam; + +export enum XAccountEndPoint { + // + //#region Test ... + Hi = 'Test/Hi', + PassReadAccess = 'Test/PassReadAccess', + PassWriteAccess = 'Test/PassWriteAccess', + PassAdminAccess = 'Test/PassAdminAccess', + PassManageAccess = 'Test/PassManageAccess', + PassRequireXPowered = 'Test/PassRequireXPowered', + HiClaims = 'Test/HiClaims', + HiUser = 'Test/HiUser', + HiEnabledUser = 'Test/HiEnabledUser', + HiColleague = 'Test/HiColleague', + HiEnabledColleague = 'Test/HiEnabledColleague', + HiAgent = 'Test/HiAgent', + HiEnabledAgent = 'Test/HiEnabledAdmingent', + HiAdmin = 'Test/HiAdmin', + HiEnabledAdmin = 'Test/HiEnabledAdmin', + //#endregion + + // + //#region Admin ... + Ban = 'Ban', + UnBan = 'UnBan', + IsBanned = 'IsBanned/{xUserSelectByParam}', + //#endregion + + // + //#region Authentication ... + DiscoveryDocument = 'DiscoveryDocument', + RequestScopeAccessToken = 'RequestScopeAccessToken', + Login = 'Login', + Logout = 'Logout', + Authenticate = 'Authenticate', + RefreshTokens = 'RefreshTokens', + Validate = 'Validate', + //#endregion + + // + //#region Registration ... + IsConfirmedEmail = 'IsConfirmedEmail', + IsConfirmedMobile = 'IsConfirmedMobile', + CanRegisterUserName = 'CanRegisterUserName/{xUserName}', + CanRegisterMobileNumber = 'CanRegisterMobileNumber/{xMobileNumber}', + CanRegisterEmail = 'CanRegisterEmail/{xEmail}', + RequestConfirmEmail = 'RequestConfirmEmail', + RequestConfirmMobile = 'RequestConfirmMobile', + RequestResetPassword = 'RequestResetPassword', + RequestConfirmRegistration = 'RequestConfirmRegistration', + ChangePassword = 'ChangePassword', + ResetPassword = 'ResetPassword', + ConfirmRegistration = 'ConfirmRegistration', + ConfirmEmailAddress = 'ConfirmEmailAddress', + ConfirmMobileNumber = 'ConfirmMobileNumber', + InviteUser = 'InviteUser', + RequestRegistration = 'RequestRegistration', + AddAccountInfo = 'AddAccountInfo', + AttachProfileImage = 'AttachProfileImage', + FinishRegistration = 'FinishRegistration', + //#endregion + + // + //#region Friendship ... + Follow = 'Friendship/{xUserSelectByParam}/Follow', + Cancel = 'Friendship/{xUserSelectByParam}/Cancel', + UnFollowFollower = 'Friendship/{xUserSelectByParam}/UnFollowFollower', + UnFollowFollowing = 'Friendship/{xUserSelectByParam}/UnFollowFollowing', + Block = 'Friendship/{xUserSelectByParam}/Block', + UnBlock = 'Friendship/{xUserSelectByParam}/UnBlock', + AcceptRequest = 'Friendship/{xUserSelectByParam}/AcceptRequest', + RejectRequest = 'Friendship/{xUserSelectByParam}/RejectRequest', + FriendshipInfo = 'Friendship/{xUserSelectByParam}/FriendshipInfo', + IsFollower = 'Friendship/{xUserSelectByParam}/IsFollower', + Follower = 'Friendship/{xUserSelectByParam}/Follower', + FollowerState = 'Friendship/{xUserSelectByParam}/FollowerState', + Followers = 'Friendship/Followers', + AllFollowers = 'Friendship/AllFollowers', + FollowersList = 'Friendship/FollowersList', + QueryFollowers = 'Friendship/QueryFollowers', + IsFollowing = 'Friendship/{xUserSelectByParam}/IsFollowing', + Following = 'Friendship/{xUserSelectByParam}/Following', + FollowingState = 'Friendship/{xUserSelectByParam}/FollowingState', + Followings = 'Friendship/Followings', + AllFollowings = 'Friendship/AllFollowings', + FollowingsList = 'Friendship/FollowingsList', + QueryFollowings = 'Friendship/QueryFollowings', + //#endregion + + // + //#region Profile ... + GetNames = 'Profile/{xIds}/GetNames', + GetNameIds = 'Profile/GetNameIds', + Profile = 'Profile/{xUserSelectByParam}', + QueryProfiles = 'Profile/Query', + QueryAvatars = 'Profile/Avatars/Query/{xUserSelectByParam}', + ProfileUpdate = 'Profile/Update/{xUserSelectByParam}', + FullProfileUpdate = 'Profile/{xUserSelectByParam}', + AddAvatar = 'Profile/Avatar', + AddAvatars = 'Profile/Avatars', + SetAvatar = 'Profile/Avatars/{xId}/Set', + RemoveAvatars = 'Profile/Avatars/{xIds}', + //#endregion + + // + //#region Search ... + QueryOpenToSearchProfiles = 'OpenToSearch/Query', + //#endregion +} + +export type XAccountEndPoints = keyof typeof XAccountEndPoint; diff --git a/projects/x-framework-identity-sdk/src/lib/typings/x-registration.typings.ts b/projects/x-framework-identity-sdk/src/lib/typings/x-registration.typings.ts new file mode 100644 index 0000000..14d48f8 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/typings/x-registration.typings.ts @@ -0,0 +1,10 @@ +export enum RegistrationStep { + Terms = 'terms', + Info = 'info', + MobileConfirmation = 'mobile_confirmation', + EmailConfirmation = 'email_confirmation', + Avatar = 'avatar', + Finish = 'finish' +} + +export type RegistrationStepIdentifier = RegistrationStep | string; diff --git a/projects/x-framework-identity-sdk/src/lib/validators/api-validators.ts b/projects/x-framework-identity-sdk/src/lib/validators/api-validators.ts new file mode 100644 index 0000000..a9724cf --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/validators/api-validators.ts @@ -0,0 +1,465 @@ +import { + tap, + concatMap, + debounceTime, + distinctUntilChanged, +} from 'rxjs/operators'; +import { + ValidatorFn, + AbstractControl, + AsyncValidatorFn, + ValidationErrors, +} from '@angular/forms'; +import { + applyLocale, + XResourceIDs, + XCountryCode, + hasUniqueChar, + stringFormatter, + isNullOrEmptyString, + normalizePhoneNumber, +} from 'x-framework-core'; +import { Subject, of, Observable } from 'rxjs'; +import { Injectable, Inject } from '@angular/core'; +import { XAccountService } from '../x-account.service'; +import { XLocalizationService } from 'x-framework-services'; +import { X_FRAMEWORK_IDENTITY_SDK_CONFIG } from '../tokens/x-injectable-tokens'; +import { XFrameworkIdentitySDKConfig } from '../config/x-framework-identity-sdk.config'; + +export interface XPasswordValidationErrorObject { + // + requireDigit?: boolean; + requireDigitErr?: boolean; + + // + requiredLength?: number; + requiredLengthErr?: boolean; + + // + requiredUniqueChars?: number; + requiredUniqueCharsErr?: boolean; + + // + requireLowercase?: boolean; + requireLowercaseErr?: boolean; + + // + requireNonAlphanumeric?: boolean; + requireNonAlphanumericErr?: boolean; + + // + requireUppercase?: boolean; + requireUppercaseErr?: boolean; +} + +export interface XPasswordValidatorError { + xPasswordValidator: XPasswordValidationErrorObject | null; +} + +@Injectable({ + providedIn: 'root', +}) +export class ApiValidators { + // + //#region Props ... + // + private userNameErrorObject = { + userNameValidator: true, + }; + + // + private emailErrorObject = { + emailValidator: true, + }; + + // + private mobileErrorObject = { + mobileValidator: true, + }; + + // + private passwordErrorObject: XPasswordValidatorError = { + xPasswordValidator: { + // + requireDigit: true, + requireDigitErr: false, + + // + requiredLength: 0, + requiredLengthErr: false, + + // + requiredUniqueChars: 0, + requiredUniqueCharsErr: false, + + // + requireLowercase: true, + requireLowercaseErr: false, + + // + requireNonAlphanumeric: true, + requireNonAlphanumericErr: false, + + // + requireUppercase: true, + requireUppercaseErr: false, + }, + }; + + // + public loading$ = new Subject(); + //#endregion + + // + //#region Constructor ... + constructor( + @Inject(X_FRAMEWORK_IDENTITY_SDK_CONFIG) + private config: XFrameworkIdentitySDKConfig, + private accountService: XAccountService, + private localizationService: XLocalizationService + ) {} + //#endregion + + // + //#region Validators ... + passwordValidator(): ValidatorFn { + // + const validatorFn = (control: AbstractControl): ValidationErrors | null => { + // + // Check Password Configs ... + if (!this.config || !this.config.passwordPolicies) { + return null; + } + + // + const pswPolicies = this.config.passwordPolicies; + + // + const errObj: XPasswordValidatorError = { + ...this.passwordErrorObject, + xPasswordValidator: { + ...this.passwordErrorObject.xPasswordValidator, + ...pswPolicies, + }, + }; + + // + const value = control && control.value ? (control.value as string) : null; + + // + // Required Length ... + if (pswPolicies.requiredLength) { + // + const isLengthPassed = + value && value.length >= pswPolicies.requiredLength; + + // + if (!isLengthPassed) { + // + errObj.xPasswordValidator.requiredLengthErr = true; + + // + return errObj; + } + } + + // + // Required Digit ... + if (pswPolicies.requireDigit) { + // + const containsDigitRegExp = new RegExp('(?=.*[0-9])'); + const isRequiredDigitPassed = containsDigitRegExp.test(value); + + // + if (!isRequiredDigitPassed) { + // + errObj.xPasswordValidator.requireDigitErr = true; + + // + return errObj; + } + } + + // + // Required LowerCase ... + if (pswPolicies.requireLowercase) { + // + const containsLowerCaseRegExp = new RegExp('(?=.*[a-z])'); + const isRequiredLowercasePassed = containsLowerCaseRegExp.test(value); + + // + if (!isRequiredLowercasePassed) { + // + errObj.xPasswordValidator.requireLowercaseErr = true; + + // + return errObj; + } + } + + // + // Required UpperCase ... + if (pswPolicies.requireUppercase) { + // + const containsUpperCaseRegExp = new RegExp('(?=.*[A-Z])'); + const isRequiredUppercasePassed = containsUpperCaseRegExp.test(value); + + // + if (!isRequiredUppercasePassed) { + // + errObj.xPasswordValidator.requireUppercaseErr = true; + + // + return errObj; + } + } + + // + // Required NonAlphabet/Special Char ... + if (pswPolicies.requireNonAlphanumeric) { + // + const containsSpecialCharsRegExp = new RegExp('(?=.*[!@#$%^&*])'); + const isRequiredSpecialCharsPassed = + containsSpecialCharsRegExp.test(value); + + // + if (!isRequiredSpecialCharsPassed) { + // + errObj.xPasswordValidator.requireNonAlphanumericErr = true; + + // + return errObj; + } + } + + // + // Required Unique Char ... + if (pswPolicies.requiredUniqueChars) { + // + const isRequiredUniqueCharPassed = hasUniqueChar(value); + + // + if (!isRequiredUniqueCharPassed) { + // + errObj.xPasswordValidator.requiredUniqueCharsErr = true; + + // + return errObj; + } + } + + // + return null; + }; + + // + return validatorFn; + } + + emailValidator(): AsyncValidatorFn { + // + const validatorFn = ( + control: AbstractControl + ): Observable => { + // + const value = control && control.value ? (control.value as string) : null; + + // + return of(null).pipe( + tap((_) => this.loading$.next(true)), + debounceTime(this.config.debounceTime), + distinctUntilChanged(), + concatMap((_) => this.accountService.canRegisterEmail(value)), + concatMap((canRegister) => + canRegister ? of(null) : of(this.emailErrorObject) + ), + tap((_) => this.loading$.next(false)) + ); + }; + + // + return validatorFn; + } + + mobileNumberValidator(defaultCountryCode?: XCountryCode): AsyncValidatorFn { + // + const validatorFn = ( + control: AbstractControl + ): Observable => { + // + const value = control && control.value ? (control.value as string) : null; + const normalValue = normalizePhoneNumber(value, defaultCountryCode); + + // + return of(null).pipe( + tap((_) => this.loading$.next(true)), + debounceTime(this.config.debounceTime), + distinctUntilChanged(), + concatMap((_) => + this.accountService.canRegisterMobileNumber(normalValue.toString()) + ), + concatMap((canRegister) => + canRegister ? of(null) : of(this.mobileErrorObject) + ), + tap((_) => this.loading$.next(false)) + ); + }; + + // + return validatorFn; + } + + userNameValidator(): AsyncValidatorFn { + // + const validatorFn = ( + control: AbstractControl + ): Observable => { + // + const value = control && control.value ? (control.value as string) : null; + + // + return of(null).pipe( + tap((_) => this.loading$.next(true)), + debounceTime(this.config.debounceTime), + distinctUntilChanged(), + concatMap((_) => this.accountService.canRegisterUserName(value)), + concatMap((canRegister) => + canRegister ? of(null) : of(this.userNameErrorObject) + ), + tap((_) => this.loading$.next(false)) + ); + }; + + // + return validatorFn; + } + //#endregion + + // + //#region Data Providers ... + /** + * generate error message for password validation ... + * + * @param error the XPasswordValidationError instance ... + */ + generatePasswordValidationError(error: XPasswordValidationErrorObject) { + // + if (!error) { + return ''; + } + + // + let errorMessage = ''; + + // + // Handle Required Length ... + if (error.requiredLength > 0 && error.requiredLengthErr) { + // + errorMessage += !isNullOrEmptyString(errorMessage) ? '\n' : ''; + errorMessage += `${this.getRequiredLengthError(error.requiredLength)}`; + } + + // + // Handle Required UniqueChars ... + else if (error.requiredUniqueChars > 0 && error.requiredUniqueCharsErr) { + // + errorMessage += !isNullOrEmptyString(errorMessage) ? '\n' : ''; + errorMessage += `${this.getRequiredUniqueChanrsError( + error.requiredUniqueChars + )}`; + } + + // + // Handle Required Digits ... + else if (error.requireDigit && error.requireDigitErr) { + // + errorMessage += !isNullOrEmptyString(errorMessage) ? '\n' : ''; + errorMessage += `${this.getRequiredDigitsError()}`; + } + + // + // Handle Required LowerCase ... + else if (error.requireLowercase && error.requireLowercaseErr) { + // + errorMessage += !isNullOrEmptyString(errorMessage) ? '\n' : ''; + errorMessage += `${this.getRequiredLowercaseError()}`; + } + + // + // Handle Required UpperCase ... + else if (error.requireUppercase && error.requireUppercaseErr) { + // + errorMessage += !isNullOrEmptyString(errorMessage) ? '\n' : ''; + errorMessage += `${this.getRequiredUppercaseError()}`; + } + + // + // Handle Required NonAlphanumeric ... + else if (error.requireNonAlphanumeric && error.requireNonAlphanumericErr) { + // + errorMessage += !isNullOrEmptyString(errorMessage) ? '\n' : ''; + errorMessage += `${this.getRequiredNonAlphanumericError()}`; + } + + // + return errorMessage; + } + //#endregion + + // + //#region Private ... + private getRequiredLengthError(length: number) { + // + const error = this.localizationService.getValue( + XResourceIDs.required_length + ); + + // + if (!error) { + return ''; + } + + // + return applyLocale( + stringFormatter(error, length), + this.localizationService.locale + ); + } + + private getRequiredUniqueChanrsError(length: number) { + // + const error = this.localizationService.getValue( + XResourceIDs.required_unique_chars + ); + + // + if (!error) { + return ''; + } + + // + return applyLocale( + stringFormatter(error, length), + this.localizationService.locale + ); + } + + private getRequiredDigitsError() { + return this.localizationService.getValue(XResourceIDs.require_digit); + } + + private getRequiredLowercaseError() { + return this.localizationService.getValue(XResourceIDs.require_lowercase); + } + + private getRequiredUppercaseError() { + return this.localizationService.getValue(XResourceIDs.require_uppercase); + } + + private getRequiredNonAlphanumericError() { + return this.localizationService.getValue( + XResourceIDs.require_non_alphanumeric + ); + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/x-account.service.ts b/projects/x-framework-identity-sdk/src/lib/x-account.service.ts new file mode 100644 index 0000000..440da46 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/x-account.service.ts @@ -0,0 +1,30 @@ +import { + X_API_CONFIG, + X_FRAMEWORK_IDENTITY_SDK_CONFIG, +} from './tokens/x-injectable-tokens'; +import { Router } from '@angular/router'; +import { HttpClient } from '@angular/common/http'; +import { Inject, Injectable } from '@angular/core'; +import { XManagerService } from 'x-framework-services'; +import { XApiConfiguration } from './config/x-api-service.config'; +import { XAccountFinalService } from './partial/x-account.final.service'; +import { XFrameworkIdentitySDKConfig } from './config/x-framework-identity-sdk.config'; + +@Injectable({ + providedIn: 'root', +}) +export class XAccountService extends XAccountFinalService { + // + //#region Constructor ... + constructor( + protected router: Router, + protected httpClient: HttpClient, + @Inject(X_FRAMEWORK_IDENTITY_SDK_CONFIG) + config: XFrameworkIdentitySDKConfig, + @Inject(X_API_CONFIG) apiConfig: XApiConfiguration, + protected managerService: XManagerService + ) { + super(router, httpClient, config, apiConfig, managerService); + } + //#endregion +} diff --git a/projects/x-framework-identity-sdk/src/lib/x-framework-identity-sdk.module.ts b/projects/x-framework-identity-sdk/src/lib/x-framework-identity-sdk.module.ts new file mode 100644 index 0000000..b683b09 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/lib/x-framework-identity-sdk.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { XFrameworkCoreModule } from 'x-framework-core'; +import { HttpClientModule } from '@angular/common/http'; +import { XFrameworkServicesModule } from 'x-framework-services'; + +@NgModule({ + declarations: [], + imports: [ + RouterModule, + CommonModule, + HttpClientModule, + XFrameworkCoreModule, + XFrameworkServicesModule, + ], + exports: [ + CommonModule, + HttpClientModule, + XFrameworkCoreModule, + XFrameworkServicesModule, + ], + providers: [], +}) +export class XFrameworkIdentitySdkModule {} diff --git a/projects/x-framework-identity-sdk/src/public-api.ts b/projects/x-framework-identity-sdk/src/public-api.ts new file mode 100644 index 0000000..a19efba --- /dev/null +++ b/projects/x-framework-identity-sdk/src/public-api.ts @@ -0,0 +1,70 @@ +/* + * Public API Surface of x-framework-identity-sdk + */ +// +// Config ... +export * from './lib/config/x-api-service.config'; +export * from './lib/config/x-framework-identity-sdk.config'; + +// +// Base ... +export * from './lib/base/x-base-api.service'; + +// +// Models ... +export * from './lib/models/x-user.dto'; +export * from './lib/models/x-page.dto'; +export * from './lib/models/x-login.dto'; +export * from './lib/models/x-query.dto'; +export * from './lib/models/x-string.dto'; +export * from './lib/models/x-friend-dto'; +export * from './lib/models/x-discovery.dto'; +export * from './lib/models/x-friendship.dto'; +export * from './lib/models/x-device-info.dto'; +export * from './lib/models/x-registration-dto'; + +// +// Guards ... +export * from './lib/guards/auth.guard'; +export * from './lib/guards/not-auth.guard'; + +// +// Services ... +export * from './lib/x-account.service'; +export * from './lib/services/api-http-interceptor.service'; + +// +// Constants ... +export * from './lib/constants/x-account.keys'; +export * from './lib/constants/x-header.enum'; +export * from './lib/constants/x-api-scope.enum'; + +// +// Validators ... +export * from './lib/validators/api-validators'; + +// +// Typings ... +export * from './lib/typings/x-account.typings'; +export * from './lib/typings/x-endpoint.typings'; +export * from './lib/typings/x-registration.typings'; + +// +// Providers ... +export * from './lib/providers/x-url.encoder'; + +// +// Tools ... +export * from './lib/tools/x-endpoint.tools'; + +// +// Helpers ... +export * from './lib/helpers/x-account.helper'; + +// +// Token ... +export * from './lib/tokens/x-injectable-tokens'; + +// +// Module ... +export * from './lib/x-framework-identity-sdk.module'; diff --git a/projects/x-framework-identity-sdk/src/test.ts b/projects/x-framework-identity-sdk/src/test.ts new file mode 100644 index 0000000..52e5516 --- /dev/null +++ b/projects/x-framework-identity-sdk/src/test.ts @@ -0,0 +1,26 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context(path: string, deep?: boolean, filter?: RegExp): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/projects/x-framework-identity-sdk/tsconfig.lib.json b/projects/x-framework-identity-sdk/tsconfig.lib.json new file mode 100644 index 0000000..1407202 --- /dev/null +++ b/projects/x-framework-identity-sdk/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/projects/x-framework-identity-sdk/tsconfig.lib.prod.json b/projects/x-framework-identity-sdk/tsconfig.lib.prod.json new file mode 100644 index 0000000..06de549 --- /dev/null +++ b/projects/x-framework-identity-sdk/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/projects/x-framework-identity-sdk/tsconfig.spec.json b/projects/x-framework-identity-sdk/tsconfig.spec.json new file mode 100644 index 0000000..715dd0a --- /dev/null +++ b/projects/x-framework-identity-sdk/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..743c0e6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,40 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "strict": true, + "baseUrl": "./", + "sourceMap": true, + "target": "es2017", + "module": "es2020", + "declaration": false, + "skipLibCheck": true, + "importHelpers": true, + "noImplicitAny": true, + "strictNullChecks": false, + "noImplicitReturns": true, + "outDir": "./dist/out-tsc", + "downlevelIteration": true, + "moduleResolution": "node", + "experimentalDecorators": true, + "noFallthroughCasesInSwitch": true, + "suppressImplicitAnyIndexErrors": true, + "forceConsistentCasingInFileNames": true, + "lib": [ + "es2018", + "dom" + ], + "paths": { + "x-framework-identity-sdk": [ + "dist/x-framework-identity-sdk/x-framework-identity-sdk", + "dist/x-framework-identity-sdk" + ] + } + }, + "angularCompilerOptions": { + "strictTemplates": true, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "enableI18nLegacyMessageIdFormat": false + } +} \ No newline at end of file diff --git a/tsconfig.lib.ivy.json b/tsconfig.lib.ivy.json new file mode 100644 index 0000000..571bd13 --- /dev/null +++ b/tsconfig.lib.ivy.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.lib.json", + "angularCompilerOptions": { + "enableIvy": true + } +} \ No newline at end of file