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

Rc 1.5.0 #291

Merged
merged 63 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
033dd51
feat: enabling cors to ensure requests to data endpoints can be made …
Sep 19, 2024
3fcb72e
chore: lint-fix
Sep 19, 2024
8514d56
feat: add charging station sequences
Sep 27, 2024
9a8f3fc
feat: generate request id using charging station sequence instead of …
Sep 27, 2024
2134e9d
version update -> 1.5.0
thanaParis Sep 30, 2024
8ea3899
Merge branch 'rc-1.5.0' into feature/enable-cors
thanaParis Sep 30, 2024
95ebe84
Merge pull request #271 from citrineos/feature/enable-cors
thanaParis Sep 30, 2024
8e6085a
Merge branch 'main' into rc-1.5.0
thanaParis Oct 1, 2024
b56c418
chore: adjust Server dependency injection
Oct 1, 2024
13c49d2
feat: extend charging station sequence types
Oct 1, 2024
7fc5f60
chore: fix build issues
Oct 1, 2024
14025fe
Merge pull request #280 from citrineos/chore/fix-build-issues
thanaParis Oct 1, 2024
0c45c38
Merge pull request #275 from citrineos/feature/station-sequences
ChrisWeissmann Oct 4, 2024
7d9ae2a
fix: remove `_` prefix so deconstruction work correctly
ChrisWeissmann Oct 4, 2024
7414cb7
feat: generate request id using charging station sequence instead of …
Sep 27, 2024
06463d9
chore: adjust Server dependency injection
Oct 1, 2024
334f97d
chore: lint fix unused vars
Oct 4, 2024
dc1b341
chore: add lib generated files to be ignored for linting.
ChrisWeissmann Oct 7, 2024
4bae93b
Merge pull request #272 from citrineos/chore/lint-fix
elliot-sabitov Oct 7, 2024
75208f1
Bumped typescript eslint to V8.7.0, added new rule for no empty objec…
Sep 25, 2024
2b0229c
Fixed warnings
Sep 26, 2024
892f4c0
Merge pull request #274 from citrineos/version/bumping-typescript-esl…
ChrisWeissmann Oct 7, 2024
21f2628
Merge branch 'feature/request-id' into feature/charging-station-seque…
thanaParis Oct 7, 2024
3988f25
Merge pull request #279 from citrineos/feature/charging-station-seque…
thanaParis Oct 7, 2024
4edc8d3
Merge pull request #276 from citrineos/feature/request-id
thanaParis Oct 7, 2024
d73910e
updating in order to work with operator ui
thanaParis Oct 18, 2024
ce0a028
feat: wip initial commit for adjusting implementation such that a Get…
Oct 18, 2024
93693a5
feat: handling Not Found or empty certificate set in GetInstalledCert…
Oct 19, 2024
241ef2f
feat: created LatestStatusNotification model and adjusting logic such…
Oct 23, 2024
7e0f6ae
Merge pull request #283 from citrineos/feature/chargingStationSequenc…
thanaParis Oct 24, 2024
166425b
fix: adjusting concurrent check to only happen after authorizations h…
Oct 28, 2024
9946714
Merge pull request #287 from citrineos/fix/transaction-event-authoriz…
thanaParis Oct 29, 2024
0c2b874
Merge pull request #285 from citrineos/feature/latest-status-notifica…
thanaParis Oct 29, 2024
b6477fe
attempting to fix with rejectUnauthorized
thanaParis Oct 31, 2024
4e3d3c9
setDataValue dodges the set function
thanaParis Oct 31, 2024
d5342bc
chore: addressing PR feedback, removing issuerKey, using CertificateH…
Nov 1, 2024
3f58aa0
Merge branch 'rc-1.5.0' into feature/persist-installed-certificate-ha…
Nov 1, 2024
6964064
feat: adjusted implementation such that the Swagger UI and the OpenAP…
Nov 4, 2024
2bc0f49
Merge pull request #284 from citrineos/feature/persist-installed-cert…
thanaParis Nov 5, 2024
bb71752
Merge pull request #288 from citrineos/bugfix/rollback-variable
thanaParis Nov 5, 2024
657f364
Adding support for SetNetworkProfile management and association with …
thanaParis Nov 5, 2024
e93aa67
merged paths
thanaParis Nov 5, 2024
6fabf6b
modifying junction table
thanaParis Nov 7, 2024
1b95a84
pr feedback
thanaParis Nov 7, 2024
db60045
Merge pull request #290 from citrineos/feature/server-network-profiles
thanaParis Nov 7, 2024
c8e4b33
switching console to logger
thanaParis Nov 7, 2024
a2247a6
Merge branch 'rc-1.5.0' into feature/49-adding-type-definitions-to-op…
thanaParis Nov 7, 2024
bef7236
merged paths
thanaParis Nov 7, 2024
7b27cc2
lint fix
thanaParis Nov 7, 2024
2916c5d
Merge pull request #289 from citrineos/feature/49-adding-type-definit…
thanaParis Nov 7, 2024
93e829a
added method to repository for setting isOnline, added calls to metho…
thanaParis Nov 11, 2024
ae8ebd1
Merge branch 'rc-1.5.0' into feature/setIsOnline
thanaParis Nov 11, 2024
d7703a1
adding repo instantiation to message router
thanaParis Nov 11, 2024
3783a3d
removing directus flow generation.
thanaParis Nov 11, 2024
00f622d
total cost: save to db
lydiazcheng Nov 12, 2024
0e9cfc7
Merge branch 'rc-1.5.0' of github.com:citrineos/citrineos-core into b…
lydiazcheng Nov 12, 2024
84788e7
Update 03_Modules/OcppRouter/src/module/router.ts
thanaParis Nov 12, 2024
1c4174e
Merge pull request #292 from citrineos/feature/setIsOnline
thanaParis Nov 12, 2024
a0e6457
total cost: fix jest tests
lydiazcheng Nov 13, 2024
687b060
fix: generate directus flows failing because full route object schema…
Nov 14, 2024
4b7d2d2
fix: TlsCertificates schema to properly set certificateChain as strin…
Nov 18, 2024
30a00c8
Merge pull request #294 from citrineos/fix/generate-directus-flows
thanaParis Nov 18, 2024
ea75667
Merge pull request #293 from citrineos/bugfix/add-total-cost
thanaParis Nov 18, 2024
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
4 changes: 2 additions & 2 deletions 00_Base/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@citrineos/base",
"version": "1.4.3",
"version": "1.5.0",
"description": "The base module for OCPP v2.0.1 including all interfaces. This module is not intended to be used directly, but rather as a dependency for other modules.",
"main": "dist/index.js",
"module": "dist/esm/index.js",
Expand Down Expand Up @@ -44,4 +44,4 @@
"uuid": "9.0.0",
"zod": "3.22.3"
}
}
}
27 changes: 19 additions & 8 deletions 00_Base/src/assertion/assertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,24 @@ export function notNull(object: any): boolean {

/**
* Ensures that obj2 contains all keys from obj1.
* @param obj1
* @param obj2
* @returns
* @param obj1
* @param obj2
* @returns
*/

export function deepDirectionalEqual(obj1: any, obj2: any, seenObjects = new WeakSet()): boolean {
export function deepDirectionalEqual(
obj1: any,
obj2: any,
seenObjects = new WeakSet(),
): boolean {
if (obj1 === obj2) return true;

if (typeof obj1 !== 'object' || obj1 === null ||
typeof obj2 !== 'object' || obj2 === null) {
if (
typeof obj1 !== 'object' ||
obj1 === null ||
typeof obj2 !== 'object' ||
obj2 === null
) {
return false;
}

Expand All @@ -53,10 +61,13 @@ export function deepDirectionalEqual(obj1: any, obj2: any, seenObjects = new Wea
const keys2 = Object.keys(obj2);

for (const key of keys1) {
if (!keys2.includes(key) || !deepDirectionalEqual(obj1[key], obj2[key], seenObjects)) {
if (
!keys2.includes(key) ||
!deepDirectionalEqual(obj1[key], obj2[key], seenObjects)
) {
return false;
}
}

return true;
}
}
1 change: 0 additions & 1 deletion 00_Base/src/config/defineConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ const getZodSchemaKeyMap = (schema: z.ZodType): Record<string, any> => {
* @param envVars The environment variables.
* @returns The merged configuration.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mergeConfigFromEnvVars<T extends Record<string, any>>(
defaultConfig: T,
envVars: NodeJS.ProcessEnv,
Expand Down
4 changes: 4 additions & 0 deletions 00_Base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ export {
MessageTypeId,
OcppError,
} from './ocpp/rpc/message';
export {
ChargingStationSequenceType,
} from './ocpp/model/enums/requestIds';
export { IFileAccess } from './interfaces/fileAccess';

// Persistence Interfaces
Expand Down Expand Up @@ -244,3 +247,4 @@ export { assert, notNull, deepDirectionalEqual } from './assertion/assertion';
export { UnauthorizedError } from './interfaces/api/exception/UnauthorizedError';
export { AuthorizationSecurity } from './interfaces/api/AuthorizationSecurity';
export { Ajv };
export declare type Constructable<T> = new (...args: any[]) => T;
195 changes: 176 additions & 19 deletions 00_Base/src/interfaces/api/AbstractModuleApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@ import {
METADATA_DATA_ENDPOINTS,
METADATA_MESSAGE_ENDPOINTS,
} from '.';
import { OcppRequest, OcppResponse, SystemConfig } from '../..';
import { MessageConfirmationSchema, OcppRequest, SystemConfig } from '../..';
import { Namespace } from '../../ocpp/persistence';
import { CallAction } from '../../ocpp/rpc/message';
import { IMessageConfirmation } from '../messages';
import { IModule } from '../modules';
import {
IMessageQuerystring,
IMessageQuerystringSchema,
} from './MessageQuerystring';
import { IMessageQuerystringSchema } from './MessageQuerystring';
import { IModuleApi } from './ModuleApi';
import { AuthorizationSecurity } from './AuthorizationSecurity';

Expand Down Expand Up @@ -62,6 +59,7 @@ export abstract class AbstractModuleApi<T extends IModule>
expose.action,
expose.method,
expose.bodySchema,
expose.optionalQuerystrings,
);
});
(
Expand Down Expand Up @@ -111,13 +109,14 @@ export abstract class AbstractModuleApi<T extends IModule>
* @param {CallAction} action - The action to be called.
* @param {Function} method - The method to be executed.
* @param {object} bodySchema - The schema for the route.
* @param {Record<string, any>} optionalQuerystrings - Optional querystrings for the route.
* @return {void}
*/
// eslint-disable-next-line @typescript-eslint/ban-types
protected _addMessageRoute(
action: CallAction,
method: (...args: any[]) => any,
bodySchema: object,
optionalQuerystrings?: Record<string, any>,
): void {
this._logger.debug(
`Adding message route for ${action}`,
Expand All @@ -127,36 +126,55 @@ export abstract class AbstractModuleApi<T extends IModule>
/**
* Executes the handler function for the given request.
*
* @param {FastifyRequest<{ Body: OcppRequest | OcppResponse, Querystring: IMessageQuerystring }>} request - The request object containing the body and querystring.
* @param {FastifyRequest<{ Body: OcppRequest, Querystring: IMessageQuerystring }>} request - The request object containing the body and querystring.
* @return {Promise<IMessageConfirmation>} The promise that resolves to the message confirmation.
*/
const _handler = async (
request: FastifyRequest<{
Body: OcppRequest | OcppResponse;
Querystring: IMessageQuerystring;
Body: OcppRequest;
Querystring: Record<string, any>;
}>,
): Promise<IMessageConfirmation> =>
method.call(
): Promise<IMessageConfirmation> => {
const { identifier, tenantId, callbackUrl, ...extraQueries } =
request.query;
return method.call(
this,
request.query.identifier,
request.query.tenantId,
identifier,
tenantId,
request.body,
request.query.callbackUrl,
callbackUrl,
Object.keys(extraQueries).length > 0 ? extraQueries : undefined,
);
};

const _opts = {
const mergedQuerySchema = {
...IMessageQuerystringSchema,
properties: {
...IMessageQuerystringSchema.properties,
...(optionalQuerystrings || {}),
},
};

const _opts: any = {
method: HttpMethod.Post,
url: this._toMessagePath(action),
handler: _handler,
schema: {
body: bodySchema,
querystring: IMessageQuerystringSchema,
querystring: mergedQuerySchema,
response: {
200: MessageConfirmationSchema,
},
} as const,
};

if (this._module.config.util.swagger?.exposeMessage) {
this._server.register(async (fastifyInstance) => {
fastifyInstance.post(this._toMessagePath(action), _opts, _handler);
this.registerSchemaForOpts(fastifyInstance, _opts);
fastifyInstance.route(_opts);
});
} else {
this._server.post(this._toMessagePath(action), _opts, _handler);
this._server.route(_opts);
}
}

Expand All @@ -168,7 +186,6 @@ export abstract class AbstractModuleApi<T extends IModule>
* @param {object} schema - The schema for the entity.
* @return {void}
*/
// eslint-disable-next-line @typescript-eslint/ban-types
protected _addDataRoute(
namespace: Namespace,
method: (...args: any[]) => any,
Expand Down Expand Up @@ -265,13 +282,153 @@ export abstract class AbstractModuleApi<T extends IModule>

if (this._module.config.util.swagger?.exposeData) {
this._server.register(async (fastifyInstance) => {
this.registerSchemaForOpts(fastifyInstance, _opts);
fastifyInstance.route<{ Body: object; Querystring: object }>(_opts);
});
} else {
this._server.route<{ Body: object; Querystring: object }>(_opts);
}
}

private registerSchemaForOpts = (
fastifyInstance: FastifyInstance,
_opts: any,
) => {
if (_opts.schema['querystring']) {
_opts.schema['querystring'] = this.registerSchema(
fastifyInstance,
_opts.schema['querystring'],
);
}
if (_opts.schema['body']) {
_opts.schema['body'] = this.registerSchema(
fastifyInstance,
_opts.schema['body'],
);
}
if (_opts.schema['params']) {
_opts.schema['params'] = this.registerSchema(
fastifyInstance,
_opts.schema['params'],
);
}
if (_opts.schema['headers']) {
_opts.schema['headers'] = this.registerSchema(
fastifyInstance,
_opts.schema['headers'],
);
}
if (_opts.schema['response']) {
_opts.schema['response'] = {
200: this.registerSchema(
fastifyInstance,
_opts.schema['response'][200],
),
};
}
};

protected registerSchema = (
fastifyInstance: FastifyInstance,
schema: any,
): object | null => {
const id = schema['$id'];
if (!id) {
this._logger.error('Could not register schema because no ID', schema);
}
try {
const schemaCopy = this.removeUnknownKeys(schema);
if (
schemaCopy.required &&
Array.isArray(schemaCopy.required) &&
schemaCopy.required.length === 0
) {
delete schemaCopy.required;
}
if (schema.definitions) {
Object.keys(schema.definitions).forEach((key) => {
const definition = schema.definitions[key];
if (!definition['$id']) {
definition['$id'] = key;
}
this.registerSchema(fastifyInstance, definition);
});
}
if (schemaCopy.properties) {
Object.keys(schemaCopy.properties).forEach((key) => {
const property = schemaCopy.properties[key];
if (property.$ref) {
property.$ref = property.$ref.replace('#/definitions/', '');
}
if (property.items && property.items.$ref) {
property.items.$ref = property.items.$ref.replace(
'#/definitions/',
'',
);
}
});
}
fastifyInstance.addSchema(schemaCopy);
this._server.addSchema(schemaCopy);
return {
$ref: `${id}`,
};
} catch (e: any) {
// ignore already declared
if (e.code === 'FST_ERR_SCH_ALREADY_PRESENT') {
return {
$ref: `${id}`,
};
} else {
this._logger.error('Could not register schema', e, schema);
}
return null;
}
};

// TODO: for performance reasons can these unknown keys be removed directly from schemas?
private removeUnknownKeys = (schema: any): any => {
// Create a deep copy of the schema
const schemaCopy = structuredClone(schema); // Use structuredClone for a true deep copy

const cleanSchema = (obj: any) => {
if (typeof obj !== 'object' || obj === null) return;

// Remove specific unknown keys
for (const unknownKey of ['comment', 'javaType', 'tsEnumNames']) {
if (unknownKey in obj) {
delete obj[unknownKey];
}
}

// Remove `additionalItems` if `items` is not an array
if (
'items' in obj &&
!Array.isArray(obj.items) &&
'additionalItems' in obj
) {
delete obj.additionalItems;
}

// Remove `additionalProperties` if `type` is not "object"
if ('additionalProperties' in obj && obj.type !== 'object') {
delete obj.additionalProperties;
}

// Recursively process nested objects
for (const key in obj) {
if (typeof obj[key] === 'object') {
cleanSchema(obj[key]);
}
}
};

// Clean the copied schema
cleanSchema(schemaCopy);

return schemaCopy;
};

/**
* Convert a {@link CallAction} to a normed lowercase URL path.
*
Expand Down
7 changes: 5 additions & 2 deletions 00_Base/src/interfaces/api/AsDataEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export const AsDataEndpoint = function (
security?: object[],
description?: string,
) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (
target: any,
propertyKey: string,
Expand All @@ -44,6 +43,10 @@ export const AsDataEndpoint = function (
METADATA_DATA_ENDPOINTS,
target.constructor,
) as Array<IDataEndpointDefinition>;
let tagList: string[] | undefined = undefined;
if (tags) {
tagList = Array.isArray(tags) ? tags : [tags];
}
dataEndpoints.push({
method: descriptor.value,
methodName: propertyKey,
Expand All @@ -54,7 +57,7 @@ export const AsDataEndpoint = function (
paramSchema: paramSchema,
headerSchema: headerSchema,
responseSchema: responseSchema,
tags: (Array.isArray(tags) ? tags : [tags]) as string[],
tags: tagList,
description: description,
security: security,
});
Expand Down
Loading