Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
24 changes: 23 additions & 1 deletion api-generator/api-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ async function main() {
const module_classes_group = module.groups.find(
(g) => g.title === 'Classes'
);
const module_tokens_group = module.groups.find(
(g) => g.title === 'Tokens'
);
// Todo: Add support for type aliases

if (isProcessable(module_components_group)) {
Expand Down Expand Up @@ -432,7 +435,7 @@ async function main() {
);
if (isProcessable(service_methods_group)) {
const methods = {
description: 'Methods used in service.',
description: 'Methods used in the service.',
values: []
};

Expand Down Expand Up @@ -466,6 +469,25 @@ async function main() {
});
}

if (isProcessable(module_tokens_group)) {
const tokens = {
description: 'Injection tokens exposed by the service.',
values: []
};

module_tokens_group.children.forEach((token) => {
tokens.values.push({
name: token.name,
type: token.type ? token.type.toString() : 'InjectionToken',
description:
token.comment &&
token.comment.summary.map((s) => s.text || '').join(' ')
});
});

doc[name].tokens = tokens;
}

if (isProcessable(module_interface_group)) {
const interfaces = {
description: staticMessages.interfaces,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"components": {},
"name": "CronValidationService",
"name": "CpsCronValidationService",
"description": "Service for validating 6-field cron expressions with extended features.\n\nThis service handles cron validation logic for extended cron expression formats\nthat support additional features beyond standard Unix cron for more flexible\nscheduling capabilities.\n\nFormat: minutes hours day-of-month month day-of-week year\n\nKey Features:\n- Wildcards: asterisk (any value), question mark (any value for day fields)\n- Ranges: 1-5, MON-FRI, JAN-MAR\n- Steps: asterisk/15, 5/10, 1-5/2\n- Lists: 1,3,5, MON,WED,FRI\n- Special chars: L (last), W (weekday), hash (nth occurrence)",
"methods": {
"description": "Methods used in service.",
"description": "Methods used in the service.",
"values": [
{
"name": "isValidCron",
Expand All @@ -24,5 +24,15 @@
"description": "Validates a complete 6-field cron expression."
}
]
},
"tokens": {
"description": "Injection tokens exposed by the service.",
"values": [
{
"name": "CPS_CRON_VALIDATION_SERVICE",
"type": "InjectionToken<CpsCronValidationService>",
"description": "Injection token for `CpsCronValidationService` .\n\nAlways inject this token instead of `CpsCronValidationService` directly.\nThis allows consumer applications to override or disable cron validation by\nproviding an alternative implementation via `providers` ."
}
]
}
}
2 changes: 1 addition & 1 deletion projects/composition/src/app/api-data/cps-dialog.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "CpsDialogService",
"description": "Service for showing CpsDialog.",
"methods": {
"description": "Methods used in service.",
"description": "Methods used in the service.",
"values": [
{
"name": "open",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "CpsNotificationService",
"description": "Service for showing notifications.",
"methods": {
"description": "Methods used in service.",
"description": "Methods used in the service.",
"values": [
{
"name": "info",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,5 +187,112 @@ <h3>{{ interface.name }}</h3>
<!-- Enums -->
<app-enums [enums]="componentData.enums"></app-enums>
}
<!-- Services -->
@for (service of services; track service.name) {
<div>
<h1>{{ service.name }}</h1>
<p>{{ service.description }}</p>
@if (service.methods.values && service.methods.values.length > 0) {
<div>
<h2>Methods</h2>
<p>{{ service.methods.description }}</p>
<div>
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>Parameters</th>
<th>Return type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
@for (method of service.methods.values; track method) {
<tr>
<td class="highlighted-bg">
<span>{{ method.name }}</span>
</td>
<td class="highlighted-text">
<div class="parameters">
@for (param of method.parameters; track param.name) {
<span>
<strong
[cpsTooltip]="param.description || ''"
tabindex="0">
{{ param.name }}:
</strong>
@if (param.type | detectType: TypesMap; as type) {
<a
[routerLink]="'/' + TypesMap[type] + '/api'"
fragment="{{ type }}">
{{ param.type }}
</a>
} @else {
<span>{{ param.type }}</span>
}
</span>
} @empty {
null
}
</div>
</td>
<td class="highlighted-text">
@if (
method.returnType | detectType: TypesMap;
as type
) {
<a
[routerLink]="'/' + TypesMap[type] + '/api'"
fragment="{{ type }}">
{{ method.returnType }}
</a>
} @else {
<span>{{ method.returnType }}</span>
}
</td>
<td class="method-description">
{{ method.description }}
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
@if (service.tokens; as tokens) {
@if (tokens.values && tokens.values.length > 0) {
<div>
<h2>Tokens</h2>
<p>{{ tokens.description }}</p>
<div>
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
@for (token of tokens.values; track token.name) {
<tr>
<td class="highlighted-bg">
<span>{{ token.name }}</span>
</td>
<td class="highlighted-text">
<span>{{ token.type }}</span>
</td>
<td>{{ token.description }}</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
}
</div>
}
</cps-tab>
</cps-tab-group>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
InterfaceAPI,
TypesAPI
} from '../../models/component-api.model';
import { CpsTabComponent, CpsTabGroupComponent } from 'cps-ui-kit';
import { ServiceAPI } from '../../models/service-api.model';
import {

Check warning on line 9 in projects/composition/src/app/components/component-docs-viewer/component-docs-viewer.component.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
CpsTabComponent,
CpsTabGroupComponent,
CpsTooltipDirective
} from 'cps-ui-kit';
import { ObjectEntriesPipe } from '../../pipes/object-entries.pipe';

import TypesMap from '../../api-data/types_map.json';
Expand All @@ -24,23 +29,26 @@
ObjectEntriesPipe,
RouterModule,
DetectTypePipe,
EnumsComponent
EnumsComponent,
CpsTooltipDirective
]
})
export class ComponentDocsViewerComponent extends ViewerComponent {
@Input() componentData?: {
components: { [key: string]: ComponentAPI };
types?: TypesAPI;
interfaces?: InterfaceAPI;
enums?: EnumsAPI;
};

@Input() services?: ServiceAPI[];

TypesMap: Record<string, string> = TypesMap;

override ngOnInit(): void {
if (!this.componentData) {
throw new Error('Input property componentData is required');
}
super.ngOnInit();
}
}

Check warning on line 54 in projects/composition/src/app/components/component-docs-viewer/component-docs-viewer.component.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ <h2>Methods</h2>
<div class="parameters">
@for (param of method.parameters; track param.name) {
<span>
<strong [cpsTooltip]="param.description || ''">
<strong
[cpsTooltip]="param.description || ''"
tabindex="0">
{{ param.name }}:
</strong>
@if (param.type | detectType: TypesMap; as type) {
Expand Down Expand Up @@ -242,7 +244,9 @@ <h4>Methods</h4>
track param.name
) {
<span>
<strong [cpsTooltip]="param.description || ''">
<strong
[cpsTooltip]="param.description || ''"
tabindex="0">
{{ param.name }}:
</strong>
@if (
Expand Down
12 changes: 12 additions & 0 deletions projects/composition/src/app/models/service-api.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,25 @@ export interface MethodAPI {
returnType: string;
}

export interface TokenAPI {
name: string;
type: string;
description: string;
}

export interface TokensAPI {
description: string;
values: TokenAPI[];
}

export interface ServiceAPI {
name: string;
description: string;
methods: {
description: string;
values: MethodAPI[];
};
tokens?: TokensAPI;
types?: TypesAPI;
interfaces?: InterfaceAPI;
classes?: ClassesAPI;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<app-component-docs-viewer [componentData]="componentData">
<app-component-docs-viewer
[componentData]="componentData"
[services]="serviceData">
<!-- Example of component's usage -->
<cps-scheduler
label="Frequency"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component } from '@angular/core';
import { CpsSchedulerComponent } from 'cps-ui-kit';
import ComponentData from '../../api-data/cps-scheduler.json';
import ServiceData from '../../api-data/cps-cron-validation.json';

Check warning on line 4 in projects/composition/src/app/pages/scheduler-page/scheduler-page.component.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
import { ComponentDocsViewerComponent } from '../../components/component-docs-viewer/component-docs-viewer.component';

@Component({
Expand All @@ -10,8 +11,9 @@
styleUrl: './scheduler-page.component.scss',
host: { class: 'composition-page' }
})
export class SchedulerPageComponent {
componentData = ComponentData;
serviceData = [ServiceData];

Check warning on line 16 in projects/composition/src/app/pages/scheduler-page/scheduler-page.component.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

cronExpression = '30 9 ? 7/4 WED#5 *';
timeZone = 'UTC';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
ReactiveFormsModule,
Validators
} from '@angular/forms';
import { CronValidationService } from '../../services/cron-validation.service';
import { CPS_CRON_VALIDATION_SERVICE } from '../../services/cps-cron-validation/cps-cron-validation.service';
import { CpsAutocompleteComponent } from '../cps-autocomplete/cps-autocomplete.component';
import {
CpsButtonToggleComponent,
Expand Down Expand Up @@ -186,7 +186,7 @@ export class CpsSchedulerComponent implements OnInit, OnChanges {

private readonly _fb = inject(FormBuilder);
private readonly _cdr = inject(ChangeDetectorRef);
private readonly _cronValidationService = inject(CronValidationService);
private readonly _cronValidationService = inject(CPS_CRON_VALIDATION_SERVICE);

activeScheduleType = 'Not set';
selectOptions = this._getSelectOptions();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { TestBed } from '@angular/core/testing';
import { CronValidationService } from './cron-validation.service';
import {
CpsCronValidationService,
CPS_CRON_VALIDATION_SERVICE
} from './cps-cron-validation.service';

describe('CronValidationService', () => {
let service: CronValidationService;
describe('CpsCronValidationService', () => {
let service: CpsCronValidationService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CronValidationService);
service = TestBed.inject(CpsCronValidationService);
});

it('should be created', () => {
Expand Down Expand Up @@ -273,4 +276,37 @@ describe('CronValidationService', () => {
expect(service.isValidCron('0 8 ? * MON#1 *')).toBe(true); // First Monday
});
});

describe('CPS_CRON_VALIDATION_SERVICE token', () => {
afterEach(() => {
TestBed.resetTestingModule();
});

it('should provide CpsCronValidationService by default', () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({});
const tokenService = TestBed.inject(CPS_CRON_VALIDATION_SERVICE);
expect(tokenService).toBeInstanceOf(CpsCronValidationService);
});

it('should allow overriding with a custom implementation', () => {
const customService = { isValidCron: () => true };
TestBed.resetTestingModule();
TestBed.configureTestingModule({
providers: [
{ provide: CPS_CRON_VALIDATION_SERVICE, useValue: customService }
]
});
const tokenService = TestBed.inject(CPS_CRON_VALIDATION_SERVICE);
expect(tokenService.isValidCron('')).toBe(true);
});

it('should delegate isValidCron to the underlying service', () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({});
const tokenService = TestBed.inject(CPS_CRON_VALIDATION_SERVICE);
expect(tokenService.isValidCron('0 12 * * ? *')).toBe(true);
expect(tokenService.isValidCron('invalid')).toBe(false);
});
});
});
Loading
Loading