Update usageCount and totalCostInUSD to use Decimal type

This commit is contained in:
Simon Larsen 2023-12-30 13:22:57 +00:00
parent 03a503b080
commit 018d3b7fcd
No known key found for this signature in database
GPG Key ID: AB45983AA9C81CDE
16 changed files with 151 additions and 28 deletions

77
Common/Types/Decimal.ts Normal file
View File

@ -0,0 +1,77 @@
// This is for Object ID for all the things in our database.
import { FindOperator } from 'typeorm';
import DatabaseProperty from './Database/DatabaseProperty';
import { JSONObject, ObjectType } from './JSON';
import BadDataException from './Exception/BadDataException';
export default class Decimal extends DatabaseProperty {
private _value: number = 0;
public get value(): number {
return this._value;
}
public set value(v: number) {
this._value = v;
}
public constructor(value: number | Decimal | string) {
super();
if (typeof value === 'string') {
value = parseFloat(value);
}
if (value instanceof Decimal) {
value = value.value;
}
this.value = value;
}
public equals(other: Decimal): boolean {
return this.value.toString() === other.value.toString();
}
public override toString(): string {
return this.value.toString();
}
protected static override toDatabase(
value: Decimal | FindOperator<Decimal>
): string | null {
if (value) {
return value.toString();
}
return null;
}
public override toJSON(): JSONObject {
return {
_type: ObjectType.Decimal,
value: (this as Decimal).toString(),
};
}
public static override fromJSON(json: JSONObject): Decimal {
if (json['_type'] === ObjectType.Decimal) {
return new Decimal((json['value'] as number) || 0);
}
throw new BadDataException('Invalid JSON: ' + JSON.stringify(json));
}
protected static override fromDatabase(_value: number): Decimal | null {
if (_value) {
return new Decimal(_value);
}
return null;
}
public static fromString(value: string): Decimal {
return new Decimal(value);
}
}

View File

@ -28,6 +28,7 @@ import StartAndEndTime from './Time/StartAndEndTime';
export enum ObjectType { export enum ObjectType {
ObjectID = 'ObjectID', ObjectID = 'ObjectID',
Decimal = 'Decimal',
Name = 'Name', Name = 'Name',
EqualToOrNull = 'EqualToOrNull', EqualToOrNull = 'EqualToOrNull',
MonitorSteps = 'MonitorSteps', MonitorSteps = 'MonitorSteps',

View File

@ -9,6 +9,7 @@ import SortOrder from 'Common/Types/BaseDatabase/SortOrder';
import { MeteredPlanUtil } from '../Types/Billing/MeteredPlan/AllMeteredPlans'; import { MeteredPlanUtil } from '../Types/Billing/MeteredPlan/AllMeteredPlans';
import MeteredPlan from 'Common/Types/Billing/MeteredPlan'; import MeteredPlan from 'Common/Types/Billing/MeteredPlan';
import ServerMeteredPlan from '../Types/Billing/MeteredPlan/ServerMeteredPlan'; import ServerMeteredPlan from '../Types/Billing/MeteredPlan/ServerMeteredPlan';
import Decimal from 'Common/Types/Decimal';
export class Service extends DatabaseService<Model> { export class Service extends DatabaseService<Model> {
public constructor(postgresDatabase?: PostgresDatabase) { public constructor(postgresDatabase?: PostgresDatabase) {
@ -94,10 +95,10 @@ export class Service extends DatabaseService<Model> {
id: usageBilling.id, id: usageBilling.id,
data: { data: {
usageCount: usageCount:
(usageBilling.usageCount || 0) + data.usageCount, new Decimal((usageBilling.usageCount?.value || 0) + data.usageCount),
totalCostInUSD: totalCostInUSD:new Decimal(
(usageBilling.totalCostInUSD || 0) + (usageBilling.totalCostInUSD?.value || 0) +
totalCostOfThisOperationInUSD, totalCostOfThisOperationInUSD),
}, },
props: { props: {
isRoot: true, isRoot: true,
@ -107,13 +108,13 @@ export class Service extends DatabaseService<Model> {
const usageBilling: Model = new Model(); const usageBilling: Model = new Model();
usageBilling.projectId = data.projectId; usageBilling.projectId = data.projectId;
usageBilling.productType = data.productType; usageBilling.productType = data.productType;
usageBilling.usageCount = data.usageCount; usageBilling.usageCount = new Decimal(data.usageCount);
usageBilling.isReportedToBillingProvider = false; usageBilling.isReportedToBillingProvider = false;
usageBilling.createdAt = OneUptimeDate.getCurrentDate(); usageBilling.createdAt = OneUptimeDate.getCurrentDate();
usageBilling.day = OneUptimeDate.getDateString( usageBilling.day = OneUptimeDate.getDateString(
OneUptimeDate.getCurrentDate() OneUptimeDate.getCurrentDate()
); );
usageBilling.totalCostInUSD = totalCostOfThisOperationInUSD; usageBilling.totalCostInUSD = new Decimal(totalCostOfThisOperationInUSD);
usageBilling.usageUnitName = meteredPlan.getUnitName(); // e.g. "GB" usageBilling.usageUnitName = meteredPlan.getUnitName(); // e.g. "GB"
await this.create({ await this.create({

View File

@ -12,6 +12,7 @@ import FieldType from 'CommonUI/src/Components/Types/FieldType';
import DashboardNavigation from '../../Utils/Navigation'; import DashboardNavigation from '../../Utils/Navigation';
import Currency from 'Common/Types/Currency'; import Currency from 'Common/Types/Currency';
import DiskSize from 'Common/Types/DiskSize'; import DiskSize from 'Common/Types/DiskSize';
import Decimal from 'Common/Types/Decimal';
export interface ComponentProps extends PageComponentProps {} export interface ComponentProps extends PageComponentProps {}
@ -93,7 +94,7 @@ const Settings: FunctionComponent<ComponentProps> = (
getElement: (item: JSONObject) => { getElement: (item: JSONObject) => {
return ( return (
<div>{`${DiskSize.convertToDecimalPlaces( <div>{`${DiskSize.convertToDecimalPlaces(
item['usageCount'] as number (item['usageCount'] as Decimal).value as number
)} ${item['usageUnitName'] as string}`}</div> )} ${item['usageUnitName'] as string}`}</div>
); );
}, },
@ -107,7 +108,7 @@ const Settings: FunctionComponent<ComponentProps> = (
getElement: (item: JSONObject) => { getElement: (item: JSONObject) => {
return ( return (
<div>{`${Currency.convertToDecimalPlaces( <div>{`${Currency.convertToDecimalPlaces(
item['totalCostInUSD'] as number (item['totalCostInUSD'] as Decimal).value as number
)} USD`}</div> )} USD`}</div>
); );
}, },

View File

@ -7,6 +7,8 @@ using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
const string endpoint = "http://localhost:4317";
Console.WriteLine($"Env var: {Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_HEADERS")?.ToString()}"); Console.WriteLine($"Env var: {Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_HEADERS")?.ToString()}");
@ -28,6 +30,15 @@ builder.Logging.AddOpenTelemetry(logging =>
.AddConsoleExporter() .AddConsoleExporter()
.AddOtlpExporter(opt => .AddOtlpExporter(opt =>
{ {
// If endpoint was not specified, the proper one will be selected according to the protocol.
if (!string.IsNullOrEmpty(endpoint))
{
opt.Endpoint = new Uri(endpoint);
// Set headers in OTLP exporter
// opt.Headers = "oneuptime-service-token=0a00ebc0-7f39-11ee-ac8c-3fb43926b224";
}
System.Console.WriteLine($"OTLP Exporter is using {opt.Protocol} protocol and endpoint {opt.Endpoint}"); System.Console.WriteLine($"OTLP Exporter is using {opt.Protocol} protocol and endpoint {opt.Endpoint}");
}); });
}); });
@ -41,6 +52,16 @@ builder.Services.AddOpenTelemetry()
.AddConsoleExporter() .AddConsoleExporter()
.AddOtlpExporter(opt => .AddOtlpExporter(opt =>
{ {
// If endpoint was not specified, the proper one will be selected according to the protocol.
if (!string.IsNullOrEmpty(endpoint))
{
opt.Endpoint = new Uri(endpoint);
// Set headers in OTLP exporter
// opt.Headers = "oneuptime-service-token=0a00ebc0-7f39-11ee-ac8c-3fb43926b224";
}
System.Console.WriteLine($"OTLP Exporter is using {opt.Protocol} protocol and endpoint {opt.Endpoint}"); System.Console.WriteLine($"OTLP Exporter is using {opt.Protocol} protocol and endpoint {opt.Endpoint}");
})); }));
@ -61,6 +82,14 @@ builder.Services.AddOpenTelemetry()
}) })
.AddOtlpExporter(opt => .AddOtlpExporter(opt =>
{ {
// If endpoint was not specified, the proper one will be selected according to the protocol.
if (!string.IsNullOrEmpty(endpoint))
{
opt.Endpoint = new Uri(endpoint);
// Set headers in OTLP exporter
// opt.Headers = "oneuptime-service-token=0a00ebc0-7f39-11ee-ac8c-3fb43926b224";
}
System.Console.WriteLine($"OTLP Exporter is using {opt.Protocol} protocol and endpoint {opt.Endpoint}"); System.Console.WriteLine($"OTLP Exporter is using {opt.Protocol} protocol and endpoint {opt.Endpoint}");
})); }));

View File

@ -40,3 +40,4 @@
2.0 2.0
2.0 2.0
2.0 2.0
2.0

View File

@ -68,8 +68,10 @@
value: {{ $.Values.port.dashboard | squote }} value: {{ $.Values.port.dashboard | squote }}
- name: ADMIN_DASHBOARD_PORT - name: ADMIN_DASHBOARD_PORT
value: {{ $.Values.port.adminDashboard | squote }} value: {{ $.Values.port.adminDashboard | squote }}
- name: OTEL_COLLECTOR_PORT - name: OTEL_COLLECTOR_GRPC_PORT
value: {{ $.Values.port.otelCollector | squote }} value: {{ $.Values.port.otelCollectorGrpc | squote }}
- name: OTEL_COLLECTOR_HTTP_PORT
value: {{ $.Values.port.otelCollectorHttp | squote }}
{{- end }} {{- end }}

View File

@ -147,7 +147,8 @@ port:
nginx: 80 nginx: 80
haraka: 2525 haraka: 2525
probe: 3500 probe: 3500
otelCollector: 4317 otelCollectorGrpc: 4317
otelCollectorHttp: 4318
testServer: testServer:

View File

@ -35,9 +35,11 @@ import { ProductType } from 'Model/Models/UsageBilling';
const LogsProto: protobuf.Root = protobuf.loadSync( const LogsProto: protobuf.Root = protobuf.loadSync(
'/usr/src/app/ProtoFiles/OTel/v1/logs.proto' '/usr/src/app/ProtoFiles/OTel/v1/logs.proto'
); );
const TracesProto: protobuf.Root = protobuf.loadSync( const TracesProto: protobuf.Root = protobuf.loadSync(
'/usr/src/app/ProtoFiles/OTel/v1/traces.proto' '/usr/src/app/ProtoFiles/OTel/v1/traces.proto'
); );
const MetricsProto: protobuf.Root = protobuf.loadSync( const MetricsProto: protobuf.Root = protobuf.loadSync(
'/usr/src/app/ProtoFiles/OTel/v1/metrics.proto' '/usr/src/app/ProtoFiles/OTel/v1/metrics.proto'
); );
@ -63,6 +65,19 @@ router.use(
'/otel/*', '/otel/*',
async (req: ExpressRequest, _res: ExpressResponse, next: NextFunction) => { async (req: ExpressRequest, _res: ExpressResponse, next: NextFunction) => {
try { try {
// check header.
const serviceTokenInHeader: string | undefined = req.headers[
'x-oneuptime-service-token'
] as string | undefined;
if (!serviceTokenInHeader) {
throw new BadRequestException(
'Missing header: x-oneuptime-service-token'
);
}
// size of req.body in bytes. // size of req.body in bytes.
const sizeInBytes: number = Buffer.byteLength( const sizeInBytes: number = Buffer.byteLength(
JSON.stringify(req.body) JSON.stringify(req.body)
@ -85,17 +100,7 @@ router.use(
throw new BadRequestException('Invalid URL: ' + req.baseUrl); throw new BadRequestException('Invalid URL: ' + req.baseUrl);
} }
// check header.
const serviceTokenInHeader: string | undefined = req.headers[
'x-oneuptime-service-token'
] as string | undefined;
if (!serviceTokenInHeader) {
throw new BadRequestException(
'Missing header: oneuptime-service-token'
);
}
const cachedServiceId: string | null = await GlobalCache.getString( const cachedServiceId: string | null = await GlobalCache.getString(
'service-token', 'service-token',
@ -276,7 +281,7 @@ router.post(
(metric['sum'] as JSONObject)['dataPoints'] && (metric['sum'] as JSONObject)['dataPoints'] &&
( (
(metric['sum'] as JSONObject)[ (metric['sum'] as JSONObject)[
'dataPoints' 'dataPoints'
] as JSONArray ] as JSONArray
).length > 0 ).length > 0
) { ) {
@ -326,7 +331,7 @@ router.post(
(metric['gauge'] as JSONObject)['dataPoints'] && (metric['gauge'] as JSONObject)['dataPoints'] &&
( (
(metric['gauge'] as JSONObject)[ (metric['gauge'] as JSONObject)[
'dataPoints' 'dataPoints'
] as JSONArray ] as JSONArray
).length > 0 ).length > 0
) { ) {
@ -377,7 +382,7 @@ router.post(
(metric['histogram'] as JSONObject)['dataPoints'] && (metric['histogram'] as JSONObject)['dataPoints'] &&
( (
(metric['histogram'] as JSONObject)[ (metric['histogram'] as JSONObject)[
'dataPoints' 'dataPoints'
] as JSONArray ] as JSONArray
).length > 0 ).length > 0
) { ) {

View File

@ -16,6 +16,7 @@ import ColumnAccessControl from 'Common/Types/Database/AccessControl/ColumnAcces
import TenantColumn from 'Common/Types/Database/TenantColumn'; import TenantColumn from 'Common/Types/Database/TenantColumn';
import TableMetadata from 'Common/Types/Database/TableMetadata'; import TableMetadata from 'Common/Types/Database/TableMetadata';
import IconProp from 'Common/Types/Icon/IconProp'; import IconProp from 'Common/Types/Icon/IconProp';
import Decimal from 'Common/Types/Decimal';
export enum ProductType { export enum ProductType {
Logs = 'Logs', Logs = 'Logs',
@ -170,8 +171,9 @@ export default class UsageBilling extends AccessControlModel {
@Column({ @Column({
nullable: false, nullable: false,
type: ColumnType.Decimal, type: ColumnType.Decimal,
transformer: Decimal.getDatabaseTransformer()
}) })
public usageCount?: number = undefined; public usageCount?: Decimal = undefined;
@ColumnAccessControl({ @ColumnAccessControl({
create: [], create: [],
@ -216,8 +218,9 @@ export default class UsageBilling extends AccessControlModel {
@Column({ @Column({
nullable: false, nullable: false,
type: ColumnType.Decimal, type: ColumnType.Decimal,
transformer: Decimal.getDatabaseTransformer()
}) })
public totalCostInUSD?: number = undefined; public totalCostInUSD?: Decimal = undefined;
@ColumnAccessControl({ @ColumnAccessControl({
create: [], create: [],

View File

@ -104,7 +104,8 @@ ACCOUNTS_PORT=3003
STATUS_PAGE_PORT=3105 STATUS_PAGE_PORT=3105
DASHBOARD_PORT=3009 DASHBOARD_PORT=3009
ADMIN_DASHBOARD_PORT=3158 ADMIN_DASHBOARD_PORT=3158
OTEL_COLLECTOR_PORT=4317 OTEL_COLLECTOR_GRPC_PORT=4317
OTEL_COLLECTOR_HTTP_PORT=4318
# If USE_INTERNAL_SMTP is true then you need to fill these values. # If USE_INTERNAL_SMTP is true then you need to fill these values.

View File

@ -44,7 +44,8 @@ x-common-variables: &common-variables
STATUS_PAGE_PORT: ${STATUS_PAGE_PORT} STATUS_PAGE_PORT: ${STATUS_PAGE_PORT}
DASHBOARD_PORT: ${DASHBOARD_PORT} DASHBOARD_PORT: ${DASHBOARD_PORT}
ADMIN_DASHBOARD_PORT: ${ADMIN_DASHBOARD_PORT} ADMIN_DASHBOARD_PORT: ${ADMIN_DASHBOARD_PORT}
OTEL_COLLECTOR_PORT: ${OTEL_COLLECTOR_PORT} OTEL_COLLECTOR_GRPC_PORT: ${OTEL_COLLECTOR_GRPC_PORT}
OTEL_COLLECTOR_HTTP_PORT: ${OTEL_COLLECTOR_HTTP_PORT}
x-common-ui-variables: &common-ui-variables x-common-ui-variables: &common-ui-variables
<<: *common-variables <<: *common-variables