Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: distributed state in AppStateService #12

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions libs/accelerator/src/lib/topic/topic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ describe('Topic', () => {
expect(values2).toEqual(['value1', 'value2'])
})

it('should has no value if message name is different', () => {
it('should have no value if message name is different', () => {
testTopic1.publish('value1')

expect(values1).toEqual(['value1'])
Expand All @@ -92,7 +92,7 @@ describe('Topic', () => {
expect(values3).toEqual([])
})

it('should has no value if message version is different', () => {
it('should have no value if message version is different', () => {
testTopic1.publish('value1')

expect(values1).toEqual(['value1'])
Expand All @@ -105,7 +105,7 @@ describe('Topic', () => {
expect(values3).toEqual([])
})

it('should has no value if message is undefined', () => {
it('should have no value if message is undefined', () => {
testTopic1.publish('value1')

expect(values1).toEqual(['value1'])
Expand Down
33 changes: 33 additions & 0 deletions libs/integration-interface/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts"],
"extends": ["plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "onecx",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "onecx",
"style": "kebab-case"
}
]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@nx/angular-template"],
"rules": {}
}
]
}
7 changes: 7 additions & 0 deletions libs/integration-interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# integration-interface

This library contains all the different Topic instances.
KimFFVII marked this conversation as resolved.
Show resolved Hide resolved

## Running unit tests

Run `nx test integration-interface` to execute the unit tests.
22 changes: 22 additions & 0 deletions libs/integration-interface/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* eslint-disable */
export default {
displayName: 'integration-interface',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../coverage/libs/integration-interface',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
}
8 changes: 8 additions & 0 deletions libs/integration-interface/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/libs/integration-interface",
"lib": {
"entryFile": "src/index.ts"
},
"assets": ["CHANGELOG.md", "./assets/**"]
}
13 changes: 13 additions & 0 deletions libs/integration-interface/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "@onecx/integration-interface",
"version": "3.1.1",
"peerDependencies": {
"@angular/common": "^15.2.7",
KimFFVII marked this conversation as resolved.
Show resolved Hide resolved
"@angular/core": "^15.2.7",
"rxjs": "^7.8.0"
},
"publishConfig": {
"access": "public"
}
}

62 changes: 62 additions & 0 deletions libs/integration-interface/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"name": "integration-interface",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/integration-interface/src",
"prefix": "onecx",
"tags": [],
"projectType": "library",
"targets": {
"build": {
"executor": "@nx/angular:package",
KimFFVII marked this conversation as resolved.
Show resolved Hide resolved
"outputs": [
"{workspaceRoot}/dist/{projectRoot}"
],
"options": {
"project": "libs/integration-interface/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "libs/integration-interface/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "libs/integration-interface/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"executor": "@nx/jest:jest",
"outputs": [
"{workspaceRoot}/coverage/{projectRoot}"
],
"options": {
"jestConfig": "libs/integration-interface/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": [
"{options.outputFile}"
],
"options": {
"lintFilePatterns": [
"libs/integration-interface/**/*.ts",
"libs/integration-interface/**/*.html"
]
}
},
"release": {
"executor": "nx-release:build-update-publish",
"options": {
"libName": "integration-interface"
}
}
}
}
6 changes: 6 additions & 0 deletions libs/integration-interface/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './lib/integration-interface.module'
export * from './lib/topics/global-error/v1/global-error.topic'
export * from './lib/topics/global-loading/v1/global-loading.topic'
export * from './lib/topics/current-mfe/v1/current-mfe.topic'
export * from './lib/topics/current-page/v1/current-page.topic'

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'

@NgModule({
imports: [CommonModule],
})
export class IntegrationInterfaceModule {}
KimFFVII marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Topic } from '@onecx/accelerator'
import { MfeInfo } from './mfe-info.model'

export class CurrentMfeTopic extends Topic<MfeInfo> {
constructor() {
super('currentMfe', 1)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface MfeInfo {
mountPath: string
version?: string
remote?: string
remoteBaseUrl: string
baseHref: string
shellName: string
displayName?: string
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { CurrentPageTopic } from './current-page.topic'

describe('CurrentPageTopic', () => {
const origAddEventListener = window.addEventListener
const origPostMessage = window.postMessage

let listeners: any[] = []
window.addEventListener = (_type: any, listener: any) => {
listeners.push(listener)
}

window.removeEventListener = (_type: any, listener: any) => {
listeners = listeners.filter((l) => l !== listener)
}

window.postMessage = (m: any) => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
listeners.forEach((l) => l({ data: m, stopImmediatePropagation: () => {}, stopPropagation: () => {} }))
}

const mutationObserverMock = jest.fn(function MutationObserver(callback) {
this.observe = jest.fn()
this.disconnect = jest.fn()
this.trigger = (mockedMutationsList: any) => {
callback(mockedMutationsList, this)
}
return this
})
global.MutationObserver = mutationObserverMock

afterAll(() => {
window.addEventListener = origAddEventListener
window.postMessage = origPostMessage
})

let values1: any[]

let testCurrentPageTopic1: CurrentPageTopic

const changeLocation = (pathName: string) => {
window.history.pushState({}, '', pathName)
mutationObserverMock.mock.instances.forEach((m) => m.trigger())
}

beforeEach(() => {
jest.restoreAllMocks()
listeners = []

values1 = []

testCurrentPageTopic1 = new CurrentPageTopic()

testCurrentPageTopic1.subscribe((v: any) => values1.push(v))
})

afterEach(() => {
jest.restoreAllMocks()
})

it('should have `/` if location is not changed initially', () => {
const expected = [
{
path: '/',
helpArticleId: 'help article id',
permission: 'permission',
pageName: 'page name',
applicationId: 'app id',
},
]

const pageInfo1 = {
path: window.location.pathname,
helpArticleId: 'help article id',
permission: 'permission',
pageName: 'page name',
applicationId: 'app id',
}

testCurrentPageTopic1.publish(pageInfo1)

expect(values1).toEqual(expected)
})

it('should have pageInfo with the correct path after changeLocation and publish', () => {
changeLocation('/test1')

const expected = [
undefined,
{
path: '/test1',
helpArticleId: 'help article id',
permission: 'permission',
pageName: 'page name',
applicationId: 'app id',
},
]

const pageInfo1 = {
path: window.location.pathname,
helpArticleId: 'help article id',
permission: 'permission',
pageName: 'page name',
applicationId: 'app id',
}

testCurrentPageTopic1.publish(pageInfo1)

expect(values1).toEqual(expected)
})

it('should have undefined if path of changeLocation do not match with path of pageInfo in publish ', () => {
changeLocation('/test1')

const expected = [undefined]

const pageInfo1 = {
path: '/test2',
helpArticleId: 'help article id',
permission: 'permission',
pageName: 'page name',
applicationId: 'app id',
}

testCurrentPageTopic1.publish(pageInfo1)

expect(values1).toEqual(expected)
})

it('should throw error if document.body is null ', () => {
const mock = jest.spyOn(document, 'querySelector')
mock.mockReturnValue(null)

expect(() => new CurrentPageTopic()).toThrowError('could not listen to location changes')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Topic } from '@onecx/accelerator'
import { combineLatest, distinctUntilChanged, map } from 'rxjs'
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'
import { PageInfo } from './page-info.model'

/**
* This topic will only fire when pageInfo.path matches document.location.pathname,
* if not it will fire undefined.
*/
export class CurrentPageTopic extends Topic<PageInfo | undefined> {
private currentPath$ = new BehaviorSubject<string>(document.location.pathname)

constructor() {
super('currentPage', 1)
this.watchForPathChanges()
}

public override asObservable() {
return combineLatest([super.asObservable(), this.currentPath$.pipe(distinctUntilChanged())]).pipe(
map(([v, path]) => ((<PageInfo>v).path === path ? v : undefined))
)
}

private watchForPathChanges() {
const body = document.querySelector('body')
if (body === null) {
console.error('could not listen to location changes')
throw new Error('could not listen to location changes')
}
const observer = new MutationObserver(() => {
this.currentPath$.next(document.location.pathname)
})
observer.observe(body, { childList: true, subtree: true })
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface PageInfo {
path: string
helpArticleId?: string
pageName?: string
permission?: string
applicationId?: string
}

Loading
Loading