feat(discord_gcp_alerts): add discord gcp alerts package (#354)
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
name: Deploy Discord GCP Alerts
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/deploy_discord_gcp_alerts.yaml
|
||||
- "packages/discord_gcp_alerts/**"
|
||||
|
||||
env:
|
||||
PROJECT_ID: code-push-prod
|
||||
SERVICE: discord-gcp-alerts
|
||||
REGION: us-central1
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: packages/discord_gcp_alerts
|
||||
|
||||
name: ☁️ Discord GCP Alerts
|
||||
|
||||
steps:
|
||||
- name: 📚 Git Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: dart-lang/setup-dart@v1
|
||||
|
||||
- name: Setup Cloud SDK
|
||||
uses: google-github-actions/setup-gcloud@v0.2.0
|
||||
with:
|
||||
project_id: ${{ env.PROJECT_ID }}
|
||||
service_account_key: ${{ secrets.CLOUD_RUN_SA_PROD }}
|
||||
export_default_credentials: true
|
||||
|
||||
- name: Authorize Docker Push
|
||||
run: gcloud auth configure-docker
|
||||
|
||||
- name: Build and Push Container
|
||||
run: |-
|
||||
docker build -t gcr.io/${{ env.PROJECT_ID }}/${{ env.SERVICE }}:${{ github.sha }} .
|
||||
docker push gcr.io/${{ env.PROJECT_ID }}/${{ env.SERVICE }}:${{ github.sha }}
|
||||
|
||||
- name: Deploy to Cloud Run
|
||||
id: deploy
|
||||
uses: google-github-actions/deploy-cloudrun@v0.4.0
|
||||
with:
|
||||
service: ${{ env.SERVICE }}
|
||||
image: gcr.io/${{ env.PROJECT_ID }}/${{ env.SERVICE }}:${{ github.sha }}
|
||||
region: ${{ env.REGION }}
|
||||
|
||||
- name: Show Output
|
||||
run: echo ${{ steps.deploy.outputs.url }}
|
||||
|
||||
- name: Ping
|
||||
run: curl "${{ steps.deploy.outputs.url }}"
|
||||
@@ -35,6 +35,10 @@ jobs:
|
||||
- ./.github/workflows/main.yaml
|
||||
- ./.github/actions/dart_package
|
||||
- packages/artifact_proxy/**
|
||||
discord_gcp_alerts:
|
||||
- ./.github/workflows/main.yaml
|
||||
- ./.github/actions/dart_package
|
||||
- packages/discord_gcp_alerts/**
|
||||
shorebird_cli:
|
||||
- ./.github/workflows/main.yaml
|
||||
- ./.github/actions/dart_package
|
||||
|
||||
@@ -28,6 +28,7 @@ This repository is a monorepo containing the following packages:
|
||||
| [shorebird_cli](packages/shorebird_cli/README.md) | Command-line which allows developers to interact with various Shorebird services |
|
||||
| [shorebird_code_push_client](packages/shorebird_code_push_client/README.md) | Dart library which allows Dart applications to interact with the ShoreBird CodePush API |
|
||||
| [shorebird_code_push_protocol](packages/shorebird_code_push_protocol/README.md) | Dart library which contains common interfaces used by Shorebird CodePush |
|
||||
| [discord_gcp_alerts](packages/discord_gcp_alerts/README.md) | Dart server which forwards GCP alerts to Discord |
|
||||
| [jwt](packages/jwt/README.md) | Dart library for verifying Json Web Tokens |
|
||||
| [updater](updater/README.md) | Rust library which handles the CodePush logic and does the real update work |
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
coverage/
|
||||
build/
|
||||
.dart_tool/
|
||||
.git/
|
||||
.github/
|
||||
.gitignore
|
||||
.idea/
|
||||
.packages
|
||||
@@ -0,0 +1,6 @@
|
||||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
# Test related files
|
||||
coverage/
|
||||
@@ -0,0 +1,21 @@
|
||||
# Use latest stable channel SDK.
|
||||
FROM dart:stable AS build
|
||||
|
||||
# Resolve app dependencies.
|
||||
WORKDIR /app
|
||||
COPY pubspec.* ./
|
||||
RUN dart pub get
|
||||
|
||||
# Copy app source code (except anything in .dockerignore) and AOT compile app.
|
||||
COPY . .
|
||||
RUN dart compile exe bin/server.dart -o bin/server
|
||||
|
||||
# Build minimal serving image from AOT-compiled `/server`
|
||||
# and the pre-built AOT-runtime in the `/runtime/` directory of the base image.
|
||||
FROM scratch
|
||||
COPY --from=build /runtime/ /
|
||||
COPY --from=build /app/bin/server /app/bin/
|
||||
|
||||
# Start server.
|
||||
EXPOSE 8080
|
||||
CMD ["/app/bin/server"]
|
||||
@@ -0,0 +1,7 @@
|
||||
# Discord GCP Alerts
|
||||
|
||||
A simple server which proxies GCP Alert notifications to a Discord webhook.
|
||||
|
||||
```
|
||||
WEBHOOK_URL=<DISCORD_WEBHOOK> dart bin/server.dart
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
include: package:very_good_analysis/analysis_options.4.0.0.yaml
|
||||
@@ -0,0 +1,21 @@
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:discord_gcp_alerts/discord_gcp_alerts.dart';
|
||||
import 'package:shelf/shelf_io.dart';
|
||||
import 'package:shelf_router/shelf_router.dart';
|
||||
|
||||
void main() async {
|
||||
final webhookUrl = Platform.environment['WEBHOOK_URL'];
|
||||
if (webhookUrl == null) {
|
||||
print('WEBHOOK_URL environment variable is not set.');
|
||||
exit(1);
|
||||
}
|
||||
final handler = gcpAlertHandler(webhookUrl: webhookUrl);
|
||||
final router = Router()..post('/', handler);
|
||||
final port = int.parse(Platform.environment['PORT'] ?? '8080');
|
||||
final ip = InternetAddress.anyIPv4;
|
||||
final server = await serve(router.call, ip, port);
|
||||
print('Server listening on port ${server.port}');
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
targets:
|
||||
$default:
|
||||
builders:
|
||||
source_gen|combining_builder:
|
||||
options:
|
||||
ignore_for_file:
|
||||
- implicit_dynamic_parameter
|
||||
- require_trailing_commas
|
||||
- cast_nullable_to_non_nullable
|
||||
- lines_longer_than_80_chars
|
||||
- unnecessary_lambdas
|
||||
json_serializable:
|
||||
options:
|
||||
field_rename: snake
|
||||
checked: true
|
||||
explicit_to_json: true
|
||||
@@ -0,0 +1,2 @@
|
||||
export 'src/gcp_alert_handler.dart';
|
||||
export 'src/models/models.dart';
|
||||
@@ -0,0 +1,47 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:discord_gcp_alerts/discord_gcp_alerts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shelf/shelf.dart';
|
||||
|
||||
/// A handler which notifies a Discord channel of a GCP alert.
|
||||
Handler gcpAlertHandler({required String webhookUrl, http.Client? client}) {
|
||||
return (Request req) async {
|
||||
final body = await req.readAsString();
|
||||
final notification = GCPAlert.fromJson(
|
||||
json.decode(body) as Map<String, dynamic>,
|
||||
);
|
||||
final incident = notification.incident;
|
||||
final summary = incident?.summary;
|
||||
final state = incident?.state;
|
||||
final url = incident?.url;
|
||||
final resource = incident?.resourceName;
|
||||
final conditionName = incident?.conditionName;
|
||||
final discordPayload = {
|
||||
'embeds': [
|
||||
{
|
||||
'title': 'GCP Alert',
|
||||
'description': summary,
|
||||
'url': url,
|
||||
'color': 0xFF0000,
|
||||
'fields': [
|
||||
{'name': 'State', 'value': state, 'inline': true},
|
||||
{'name': 'Resource', 'value': resource, 'inline': true},
|
||||
{'name': 'Condition', 'value': conditionName, 'inline': true},
|
||||
]
|
||||
}
|
||||
],
|
||||
'attachments': const <dynamic>[]
|
||||
};
|
||||
|
||||
final post = client?.post ?? http.post;
|
||||
final response = await post(
|
||||
Uri.parse(webhookUrl),
|
||||
headers: {HttpHeaders.contentTypeHeader: ContentType.json.value},
|
||||
body: json.encode(discordPayload),
|
||||
);
|
||||
|
||||
return Response(response.statusCode, body: response.body);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'gcp_alert.g.dart';
|
||||
|
||||
/// {@template gcp_alert}
|
||||
/// A GCP alert notification.
|
||||
/// https://console.cloud.google.com/monitoring/alerting/notifications
|
||||
///
|
||||
/// For sample alerts, see https://cloud.google.com/monitoring/alerts/policies-in-json.
|
||||
/// {@endtemplate}
|
||||
@JsonSerializable()
|
||||
class GCPAlert {
|
||||
/// {@macro gcp_alert}
|
||||
const GCPAlert({this.version, this.incident});
|
||||
|
||||
/// Creates a [GCPAlert] from a JSON [Map].
|
||||
factory GCPAlert.fromJson(Map<String, dynamic> json) =>
|
||||
_$GCPAlertFromJson(json);
|
||||
|
||||
/// Converts a [GCPAlert] to the JSON representation.
|
||||
Map<String, dynamic> toJson() => _$GCPAlertToJson(this);
|
||||
|
||||
/// The version of the alert.
|
||||
final String? version;
|
||||
|
||||
/// The incident associated with the alert.
|
||||
final Incident? incident;
|
||||
}
|
||||
|
||||
/// {@template incident}
|
||||
/// An incident which triggered an alert.
|
||||
/// {@endtemplate}
|
||||
@JsonSerializable()
|
||||
class Incident {
|
||||
/// {@macro incident}
|
||||
const Incident({
|
||||
this.incidentId,
|
||||
this.scopingProjectId,
|
||||
this.scopingProjectNumber,
|
||||
this.url,
|
||||
this.startedAt,
|
||||
this.endedAt,
|
||||
this.state,
|
||||
this.summary,
|
||||
this.observedValue,
|
||||
this.resource,
|
||||
this.resourceTypeDisplayName,
|
||||
this.resourceId,
|
||||
this.resourceDisplayName,
|
||||
this.resourceName,
|
||||
this.metric,
|
||||
this.policyName,
|
||||
this.documentation,
|
||||
this.condition,
|
||||
this.conditionName,
|
||||
this.thresholdValue,
|
||||
});
|
||||
|
||||
/// Creates an [Incident] from a JSON [Map].
|
||||
factory Incident.fromJson(Map<String, dynamic> json) =>
|
||||
_$IncidentFromJson(json);
|
||||
|
||||
/// Converts an [Incident] to the JSON representation.
|
||||
Map<String, dynamic> toJson() => _$IncidentToJson(this);
|
||||
|
||||
/// The unique identifier of the incident.
|
||||
final String? incidentId;
|
||||
|
||||
/// The project ID of the scoping project.
|
||||
final String? scopingProjectId;
|
||||
|
||||
/// The project number of the scoping project.
|
||||
final int? scopingProjectNumber;
|
||||
|
||||
/// The URL of the incident.
|
||||
final String? url;
|
||||
|
||||
/// The time the incident started.
|
||||
final int? startedAt;
|
||||
|
||||
/// The time the incident ended.
|
||||
final int? endedAt;
|
||||
|
||||
/// The state of the incident.
|
||||
final String? state;
|
||||
|
||||
/// A summary of the incident.
|
||||
final String? summary;
|
||||
|
||||
/// The observed value of the incident.
|
||||
final String? observedValue;
|
||||
|
||||
/// The resource associated with the incident.
|
||||
final Resource? resource;
|
||||
|
||||
/// The display name of the resource type.
|
||||
final String? resourceTypeDisplayName;
|
||||
|
||||
/// The unique identifier of the resource.
|
||||
final String? resourceId;
|
||||
|
||||
/// The display name of the resource.
|
||||
final String? resourceDisplayName;
|
||||
|
||||
/// The name of the resource.
|
||||
final String? resourceName;
|
||||
|
||||
/// The metric associated with the incident.
|
||||
final Metric? metric;
|
||||
|
||||
/// The name of the policy associated with the incident.
|
||||
final String? policyName;
|
||||
|
||||
/// The documentation associated with the incident.
|
||||
final String? documentation;
|
||||
|
||||
/// The condition associated with the incident.
|
||||
final Condition? condition;
|
||||
|
||||
/// The name of the condition associated with the incident.
|
||||
final String? conditionName;
|
||||
|
||||
/// The threshold value of the incident.
|
||||
final String? thresholdValue;
|
||||
}
|
||||
|
||||
/// {@template resource}
|
||||
/// A resource associated with an incident.
|
||||
/// {@endtemplate}
|
||||
@JsonSerializable()
|
||||
class Resource {
|
||||
/// {@macro resource}
|
||||
const Resource({this.type});
|
||||
|
||||
/// Creates a [Resource] from a JSON [Map].
|
||||
factory Resource.fromJson(Map<String, dynamic> json) =>
|
||||
_$ResourceFromJson(json);
|
||||
|
||||
/// Converts a [Resource] to the JSON representation.
|
||||
Map<String, dynamic> toJson() => _$ResourceToJson(this);
|
||||
|
||||
/// The type of the resource.
|
||||
final String? type;
|
||||
}
|
||||
|
||||
/// {@template metric}
|
||||
/// A metric associated with an incident.
|
||||
/// {@endtemplate}
|
||||
@JsonSerializable()
|
||||
class Metric {
|
||||
/// {@macro metric}
|
||||
const Metric({this.type, this.displayName});
|
||||
|
||||
/// Creates a [Metric] from a JSON [Map].
|
||||
factory Metric.fromJson(Map<String, dynamic> json) => _$MetricFromJson(json);
|
||||
|
||||
/// Converts a [Metric] to the JSON representation.
|
||||
Map<String, dynamic> toJson() => _$MetricToJson(this);
|
||||
|
||||
/// The type of the metric.
|
||||
final String? type;
|
||||
|
||||
/// The display name of the metric.
|
||||
final String? displayName;
|
||||
}
|
||||
|
||||
/// {@template condition}
|
||||
/// A condition associated with an incident.
|
||||
/// {@endtemplate}
|
||||
@JsonSerializable()
|
||||
class Condition {
|
||||
/// {@macro condition}
|
||||
const Condition({this.name, this.displayName, this.conditionThreshold});
|
||||
|
||||
/// Creates a [Condition] from a JSON [Map].
|
||||
factory Condition.fromJson(Map<String, dynamic> json) =>
|
||||
_$ConditionFromJson(json);
|
||||
|
||||
/// Converts a [Condition] to the JSON representation.
|
||||
Map<String, dynamic> toJson() => _$ConditionToJson(this);
|
||||
|
||||
/// The name of the condition.
|
||||
final String? name;
|
||||
|
||||
/// The display name of the condition.
|
||||
final String? displayName;
|
||||
|
||||
/// The condition threshold.
|
||||
final ConditionThreshold? conditionThreshold;
|
||||
}
|
||||
|
||||
/// {@template condition_threshold}
|
||||
/// The condition threshold associated with an incident.
|
||||
/// {@endtemplate}
|
||||
@JsonSerializable()
|
||||
class ConditionThreshold {
|
||||
/// {@macro condition_threshold}
|
||||
const ConditionThreshold({
|
||||
this.filter,
|
||||
this.comparison,
|
||||
this.thresholdValue,
|
||||
this.duration,
|
||||
this.trigger,
|
||||
});
|
||||
|
||||
/// Creates a [ConditionThreshold] from a JSON [Map].
|
||||
factory ConditionThreshold.fromJson(Map<String, dynamic> json) =>
|
||||
_$ConditionThresholdFromJson(json);
|
||||
|
||||
/// Converts a [ConditionThreshold] to the JSON representation.
|
||||
Map<String, dynamic> toJson() => _$ConditionThresholdToJson(this);
|
||||
|
||||
/// The filter associated with the condition threshold.
|
||||
final String? filter;
|
||||
|
||||
/// The comparison associated with the condition threshold.
|
||||
final String? comparison;
|
||||
|
||||
/// The threshold value associated with the condition threshold.
|
||||
final double? thresholdValue;
|
||||
|
||||
/// The duration associated with the condition threshold.
|
||||
final String? duration;
|
||||
|
||||
/// The trigger associated with the condition threshold.
|
||||
final Trigger? trigger;
|
||||
}
|
||||
|
||||
/// {@template trigger}
|
||||
/// The trigger associated with an incident.
|
||||
/// {@endtemplate}
|
||||
@JsonSerializable()
|
||||
class Trigger {
|
||||
/// {@macro trigger}
|
||||
const Trigger({this.count});
|
||||
|
||||
/// Creates a [Trigger] from a JSON [Map].
|
||||
factory Trigger.fromJson(Map<String, dynamic> json) =>
|
||||
_$TriggerFromJson(json);
|
||||
|
||||
/// Converts a [Trigger] to the JSON representation.
|
||||
Map<String, dynamic> toJson() => _$TriggerToJson(this);
|
||||
|
||||
/// The count associated with the trigger.
|
||||
final int? count;
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
// ignore_for_file: implicit_dynamic_parameter, require_trailing_commas, cast_nullable_to_non_nullable, lines_longer_than_80_chars, unnecessary_lambdas
|
||||
|
||||
part of 'gcp_alert.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
GCPAlert _$GCPAlertFromJson(Map<String, dynamic> json) => $checkedCreate(
|
||||
'GCPAlert',
|
||||
json,
|
||||
($checkedConvert) {
|
||||
final val = GCPAlert(
|
||||
version: $checkedConvert('version', (v) => v as String?),
|
||||
incident: $checkedConvert(
|
||||
'incident',
|
||||
(v) => v == null
|
||||
? null
|
||||
: Incident.fromJson(v as Map<String, dynamic>)),
|
||||
);
|
||||
return val;
|
||||
},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GCPAlertToJson(GCPAlert instance) => <String, dynamic>{
|
||||
'version': instance.version,
|
||||
'incident': instance.incident?.toJson(),
|
||||
};
|
||||
|
||||
Incident _$IncidentFromJson(Map<String, dynamic> json) => $checkedCreate(
|
||||
'Incident',
|
||||
json,
|
||||
($checkedConvert) {
|
||||
final val = Incident(
|
||||
incidentId: $checkedConvert('incident_id', (v) => v as String?),
|
||||
scopingProjectId:
|
||||
$checkedConvert('scoping_project_id', (v) => v as String?),
|
||||
scopingProjectNumber:
|
||||
$checkedConvert('scoping_project_number', (v) => v as int?),
|
||||
url: $checkedConvert('url', (v) => v as String?),
|
||||
startedAt: $checkedConvert('started_at', (v) => v as int?),
|
||||
endedAt: $checkedConvert('ended_at', (v) => v as int?),
|
||||
state: $checkedConvert('state', (v) => v as String?),
|
||||
summary: $checkedConvert('summary', (v) => v as String?),
|
||||
observedValue: $checkedConvert('observed_value', (v) => v as String?),
|
||||
resource: $checkedConvert(
|
||||
'resource',
|
||||
(v) => v == null
|
||||
? null
|
||||
: Resource.fromJson(v as Map<String, dynamic>)),
|
||||
resourceTypeDisplayName: $checkedConvert(
|
||||
'resource_type_display_name', (v) => v as String?),
|
||||
resourceId: $checkedConvert('resource_id', (v) => v as String?),
|
||||
resourceDisplayName:
|
||||
$checkedConvert('resource_display_name', (v) => v as String?),
|
||||
resourceName: $checkedConvert('resource_name', (v) => v as String?),
|
||||
metric: $checkedConvert(
|
||||
'metric',
|
||||
(v) => v == null
|
||||
? null
|
||||
: Metric.fromJson(v as Map<String, dynamic>)),
|
||||
policyName: $checkedConvert('policy_name', (v) => v as String?),
|
||||
documentation: $checkedConvert('documentation', (v) => v as String?),
|
||||
condition: $checkedConvert(
|
||||
'condition',
|
||||
(v) => v == null
|
||||
? null
|
||||
: Condition.fromJson(v as Map<String, dynamic>)),
|
||||
conditionName: $checkedConvert('condition_name', (v) => v as String?),
|
||||
thresholdValue:
|
||||
$checkedConvert('threshold_value', (v) => v as String?),
|
||||
);
|
||||
return val;
|
||||
},
|
||||
fieldKeyMap: const {
|
||||
'incidentId': 'incident_id',
|
||||
'scopingProjectId': 'scoping_project_id',
|
||||
'scopingProjectNumber': 'scoping_project_number',
|
||||
'startedAt': 'started_at',
|
||||
'endedAt': 'ended_at',
|
||||
'observedValue': 'observed_value',
|
||||
'resourceTypeDisplayName': 'resource_type_display_name',
|
||||
'resourceId': 'resource_id',
|
||||
'resourceDisplayName': 'resource_display_name',
|
||||
'resourceName': 'resource_name',
|
||||
'policyName': 'policy_name',
|
||||
'conditionName': 'condition_name',
|
||||
'thresholdValue': 'threshold_value'
|
||||
},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$IncidentToJson(Incident instance) => <String, dynamic>{
|
||||
'incident_id': instance.incidentId,
|
||||
'scoping_project_id': instance.scopingProjectId,
|
||||
'scoping_project_number': instance.scopingProjectNumber,
|
||||
'url': instance.url,
|
||||
'started_at': instance.startedAt,
|
||||
'ended_at': instance.endedAt,
|
||||
'state': instance.state,
|
||||
'summary': instance.summary,
|
||||
'observed_value': instance.observedValue,
|
||||
'resource': instance.resource?.toJson(),
|
||||
'resource_type_display_name': instance.resourceTypeDisplayName,
|
||||
'resource_id': instance.resourceId,
|
||||
'resource_display_name': instance.resourceDisplayName,
|
||||
'resource_name': instance.resourceName,
|
||||
'metric': instance.metric?.toJson(),
|
||||
'policy_name': instance.policyName,
|
||||
'documentation': instance.documentation,
|
||||
'condition': instance.condition?.toJson(),
|
||||
'condition_name': instance.conditionName,
|
||||
'threshold_value': instance.thresholdValue,
|
||||
};
|
||||
|
||||
Resource _$ResourceFromJson(Map<String, dynamic> json) => $checkedCreate(
|
||||
'Resource',
|
||||
json,
|
||||
($checkedConvert) {
|
||||
final val = Resource(
|
||||
type: $checkedConvert('type', (v) => v as String?),
|
||||
);
|
||||
return val;
|
||||
},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ResourceToJson(Resource instance) => <String, dynamic>{
|
||||
'type': instance.type,
|
||||
};
|
||||
|
||||
Metric _$MetricFromJson(Map<String, dynamic> json) => $checkedCreate(
|
||||
'Metric',
|
||||
json,
|
||||
($checkedConvert) {
|
||||
final val = Metric(
|
||||
type: $checkedConvert('type', (v) => v as String?),
|
||||
displayName: $checkedConvert('display_name', (v) => v as String?),
|
||||
);
|
||||
return val;
|
||||
},
|
||||
fieldKeyMap: const {'displayName': 'display_name'},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MetricToJson(Metric instance) => <String, dynamic>{
|
||||
'type': instance.type,
|
||||
'display_name': instance.displayName,
|
||||
};
|
||||
|
||||
Condition _$ConditionFromJson(Map<String, dynamic> json) => $checkedCreate(
|
||||
'Condition',
|
||||
json,
|
||||
($checkedConvert) {
|
||||
final val = Condition(
|
||||
name: $checkedConvert('name', (v) => v as String?),
|
||||
displayName: $checkedConvert('display_name', (v) => v as String?),
|
||||
conditionThreshold: $checkedConvert(
|
||||
'condition_threshold',
|
||||
(v) => v == null
|
||||
? null
|
||||
: ConditionThreshold.fromJson(v as Map<String, dynamic>)),
|
||||
);
|
||||
return val;
|
||||
},
|
||||
fieldKeyMap: const {
|
||||
'displayName': 'display_name',
|
||||
'conditionThreshold': 'condition_threshold'
|
||||
},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ConditionToJson(Condition instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
'display_name': instance.displayName,
|
||||
'condition_threshold': instance.conditionThreshold?.toJson(),
|
||||
};
|
||||
|
||||
ConditionThreshold _$ConditionThresholdFromJson(Map<String, dynamic> json) =>
|
||||
$checkedCreate(
|
||||
'ConditionThreshold',
|
||||
json,
|
||||
($checkedConvert) {
|
||||
final val = ConditionThreshold(
|
||||
filter: $checkedConvert('filter', (v) => v as String?),
|
||||
comparison: $checkedConvert('comparison', (v) => v as String?),
|
||||
thresholdValue: $checkedConvert(
|
||||
'threshold_value', (v) => (v as num?)?.toDouble()),
|
||||
duration: $checkedConvert('duration', (v) => v as String?),
|
||||
trigger: $checkedConvert(
|
||||
'trigger',
|
||||
(v) => v == null
|
||||
? null
|
||||
: Trigger.fromJson(v as Map<String, dynamic>)),
|
||||
);
|
||||
return val;
|
||||
},
|
||||
fieldKeyMap: const {'thresholdValue': 'threshold_value'},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ConditionThresholdToJson(ConditionThreshold instance) =>
|
||||
<String, dynamic>{
|
||||
'filter': instance.filter,
|
||||
'comparison': instance.comparison,
|
||||
'threshold_value': instance.thresholdValue,
|
||||
'duration': instance.duration,
|
||||
'trigger': instance.trigger?.toJson(),
|
||||
};
|
||||
|
||||
Trigger _$TriggerFromJson(Map<String, dynamic> json) => $checkedCreate(
|
||||
'Trigger',
|
||||
json,
|
||||
($checkedConvert) {
|
||||
final val = Trigger(
|
||||
count: $checkedConvert('count', (v) => v as int?),
|
||||
);
|
||||
return val;
|
||||
},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$TriggerToJson(Trigger instance) => <String, dynamic>{
|
||||
'count': instance.count,
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export 'gcp_alert.dart';
|
||||
@@ -0,0 +1,565 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: a36ec4843dc30ea6bf652bf25e3448db6c5e8bcf4aa55f063a5d1dad216d8214
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "58.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: cc4242565347e98424ce9945c819c192ec0838cb9d1f6aa4a97cc96becbc5b27
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.10.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.7"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_collection
|
||||
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
built_value:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.4.4"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.3"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "6d691edde054969f0e0f26abb1b30834b5138b963793e56f69d3a9a4435e6352"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.5"
|
||||
http_methods:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_methods
|
||||
sha256: c192bb6fb4ae99d06053f67a2c1c65350a29bc778a39d9a12b96bd2ec820e9dc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
json_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.0"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: dadc08bd61f72559f938dd08ec20dbfec6c709bba83515085ea943d2078d187a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.6.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.15"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
mocktail:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mocktail
|
||||
sha256: "80a996cd9a69284b3dc521ce185ffe9150cde69767c2d3a0720147d93c0cef53"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_preamble
|
||||
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
shelf:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shelf
|
||||
sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_packages_handler
|
||||
sha256: aef74dc9195746a384843102142ab65b6a4735bb3beea791e63527b88cc83306
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
shelf_router:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shelf_router
|
||||
sha256: "0b0bfb835e8b2bb43c5341ee689f0d2851e9cea377a4f2db4ec06a1a99beace4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
shelf_static:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_static
|
||||
sha256: e792b76b96a36d4a41b819da593aff4bdd413576b3ba6150df5d8d9996d2e74c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
source_gen:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: c2bea18c95cfa0276a366270afaa2850b09b4a76db95d546f3d003dcc7011298
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.7"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_map_stack_trace
|
||||
sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_maps
|
||||
sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.12"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_transform
|
||||
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
sha256: "4f92f103ef63b1bbac6f4bd1930624fca81b2574464482512c4f0896319be575"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.24.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: daadc9baabec998b062c9091525aa95786508b1c48e9c30f1f891b8bf6ff2e64
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.2"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "3642b184882f79e76ca57a9230fb971e494c3c1fd09c21ae3083ce891bcc0aa1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.2"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
very_good_analysis:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: very_good_analysis
|
||||
sha256: ebc48c51db35beeeec8c414e32f7bd78e612bd7f5992ccb0d46e19edaeb40b08
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0+1"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "518254c0d3ee20667a1feef39eefe037df87439851e4b3cb277e5b3f37afa2f0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.4.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
sdks:
|
||||
dart: ">=2.19.6 <3.0.0"
|
||||
@@ -0,0 +1,20 @@
|
||||
name: discord_gcp_alerts
|
||||
description: A server which proxies GCP Alert notifications to Discord.
|
||||
version: 1.0.0
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: '>=2.19.6 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
http: ^0.13.5
|
||||
json_annotation: ^4.8.0
|
||||
shelf: ^1.4.0
|
||||
shelf_router: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.3.3
|
||||
json_serializable: ^6.6.1
|
||||
mocktail: ^0.3.0
|
||||
test: ^1.21.0
|
||||
very_good_analysis: ^4.0.0
|
||||
@@ -0,0 +1,122 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:discord_gcp_alerts/discord_gcp_alerts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:shelf/shelf.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockHttpClient extends Mock implements http.Client {}
|
||||
|
||||
void main() {
|
||||
group('gcpAlertHandler', () {
|
||||
const webHookUrl = 'http://example.com/webhook';
|
||||
late http.Client client;
|
||||
late Handler handler;
|
||||
|
||||
setUp(() {
|
||||
client = _MockHttpClient();
|
||||
handler = gcpAlertHandler(webhookUrl: webHookUrl, client: client);
|
||||
});
|
||||
|
||||
test('forwards the request to the webhook', () async {
|
||||
when(
|
||||
() => client.post(
|
||||
Uri.parse(webHookUrl),
|
||||
headers: any(named: 'headers'),
|
||||
body: any(named: 'body'),
|
||||
),
|
||||
).thenAnswer((_) async => http.Response('', 200));
|
||||
|
||||
await handler(
|
||||
Request(
|
||||
'POST',
|
||||
Uri.parse('http://localhost:8080/'),
|
||||
body: json.encode(
|
||||
{
|
||||
'version': 'test',
|
||||
'incident': {
|
||||
'incident_id': '12345',
|
||||
'scoping_project_id': '12345',
|
||||
'scoping_project_number': 12345,
|
||||
'url': 'http://www.example.com',
|
||||
'started_at': 0,
|
||||
'ended_at': 0,
|
||||
'state': 'OPEN',
|
||||
'summary': 'Test Incident',
|
||||
'apigee_url': 'http://www.example.com',
|
||||
'observed_value': '1.0',
|
||||
'resource': {
|
||||
'type': 'example_resource',
|
||||
'labels': {'example': 'label'}
|
||||
},
|
||||
'resource_type_display_name': 'Example Resource Type',
|
||||
'resource_id': '12345',
|
||||
'resource_display_name': 'Example Resource',
|
||||
'resource_name': 'projects/12345/example_resources/12345',
|
||||
'metric': {
|
||||
'type': 'test.googleapis.com/metric',
|
||||
'displayName': 'Test Metric',
|
||||
'labels': {'example': 'label'}
|
||||
},
|
||||
'metadata': {
|
||||
'system_labels': {'example': 'label'},
|
||||
'user_labels': {'example': 'label'}
|
||||
},
|
||||
'policy_name': 'projects/12345/alertPolicies/12345',
|
||||
'policy_user_labels': {'example': 'label'},
|
||||
'documentation': 'Test documentation',
|
||||
'condition': {
|
||||
'name': 'projects/12345/alertPolicies/12345/conditions/12345',
|
||||
'displayName': 'Example condition',
|
||||
'conditionThreshold': {
|
||||
'filter':
|
||||
'metric.type=\"test.googleapis.com/metric\" resource.type=\"example_resource\"',
|
||||
'comparison': 'COMPARISON_GT',
|
||||
'thresholdValue': 0.5,
|
||||
'duration': '0s',
|
||||
'trigger': {'count': 1}
|
||||
}
|
||||
},
|
||||
'condition_name': 'Example condition',
|
||||
'threshold_value': '0.5'
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
verify(
|
||||
() => client.post(
|
||||
Uri.parse(webHookUrl),
|
||||
headers: {HttpHeaders.contentTypeHeader: ContentType.json.value},
|
||||
body: json.encode({
|
||||
'embeds': [
|
||||
{
|
||||
'title': 'GCP Alert',
|
||||
'description': 'Test Incident',
|
||||
'url': 'http://www.example.com',
|
||||
'color': 16711680,
|
||||
'fields': [
|
||||
{'name': 'State', 'value': 'OPEN', 'inline': true},
|
||||
{
|
||||
'name': 'Resource',
|
||||
'value': 'projects/12345/example_resources/12345',
|
||||
'inline': true
|
||||
},
|
||||
{
|
||||
'name': 'Condition',
|
||||
'value': 'Example condition',
|
||||
'inline': true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'attachments': <dynamic>[]
|
||||
}),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import 'package:discord_gcp_alerts/discord_gcp_alerts.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('GCPAlert', () {
|
||||
test('can be (de)serialized', () {
|
||||
const alert = GCPAlert(
|
||||
version: '1.0.0',
|
||||
incident: Incident(
|
||||
condition: Condition(
|
||||
name: 'test',
|
||||
displayName: 'Test Condition',
|
||||
conditionThreshold: ConditionThreshold(
|
||||
trigger: Trigger(count: 10),
|
||||
filter: 'metric.type = "test.googleapis.com/metric"',
|
||||
comparison: 'ComparisonType.greaterThan',
|
||||
thresholdValue: 1,
|
||||
duration: '0s',
|
||||
),
|
||||
),
|
||||
resource: Resource(type: 'test_resource'),
|
||||
metric: Metric(
|
||||
type: 'test.googleapis.com/metric',
|
||||
displayName: 'Test Metric',
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
GCPAlert.fromJson(alert.toJson()).toJson(),
|
||||
equals(alert.toJson()),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user