Skip to content

Commit

Permalink
Stop authenticating healthchecks and automatically handle the NDC ver…
Browse files Browse the repository at this point in the history
…sion in capabilities (#36)

* Stop applying authorization to the health readiness check
* Return the correct NDC version in the capabilities automatically
* Bump version to 6.0.0
* Update changelog
  • Loading branch information
daniel-chambers authored Aug 8, 2024
1 parent 5f2fc58 commit 40448c6
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 26 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# NDC TypeScript SDK Changelog

## [6.0.0] - 2024-08-08
Breaking changes ([#36](https://github.com/hasura/ndc-sdk-typescript/pull/36)):
- `Connector.healthCheck` has been removed and replaced with `Connector.getHealthReadiness`, which only returns whether the connector is able to accept requests, not whether any underlying connections to data sources actually work.
- The `/health` endpoint is now unauthorized, allowing healthchecks to be performed without authorization.
- `Connector.getCapabilities` now returns `Capabilities` instead of `CapabilitiesResponse`. The SDK will now take care of adding the correct NDC version to the `Capabilities` on behalf of the connector.

## [5.2.0] - 2024-07-30
- The connector now listens on both ipv4 and ipv6 interfaces by default. This can be configured by using the `HASURA_CONNECTOR_HOST` environment variable, which sets the host the web server listens on. ([#34](https://github.com/hasura/ndc-sdk-typescript/pull/34))
- Updated to support [v0.1.5 of the NDC Spec](https://hasura.github.io/ndc-spec/specification/changelog.html#015) ([#35](https://github.com/hasura/ndc-sdk-typescript/pull/35))
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hasura/ndc-sdk-typescript",
"version": "5.2.0",
"version": "6.0.0",
"description": "",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down
19 changes: 12 additions & 7 deletions src/connector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Registry } from "prom-client";
import {
CapabilitiesResponse,
Capabilities,
QueryRequest,
QueryResponse,
SchemaResponse,
Expand Down Expand Up @@ -35,6 +35,7 @@ export interface Connector<Configuration, State> {
configuration: Configuration,
metrics: Registry
): Promise<State>;

/**
*
* Update any metrics from the state
Expand All @@ -48,28 +49,32 @@ export interface Connector<Configuration, State> {
* @param state
*/
fetchMetrics(configuration: Configuration, state: State): Promise<undefined>;

/**
* Check the health of the connector.
*
* For example, this function should check that the connector
* is able to reach its data source over the network.
* This should simply verify that the connector is ready to start accepting
* requests. It should not verify that external data sources are available.
*
* This function is optional to implement; if left unimplemented, a default
* implementation will be used that returns healthy once the connector
* webserver is running.
*
* Should throw if the check fails, else resolve
* @param configuration
* @param state
*/
healthCheck(configuration: Configuration, state: State): Promise<undefined>;
getHealthReadiness?(configuration: Configuration, state: State): Promise<undefined>;

/**
* Get the connector's capabilities.
*
* This function implements the [capabilities endpoint](https://hasura.github.io/ndc-spec/specification/capabilities.html)
* from the NDC specification.
*
* This function should be syncronous
* This function should be synchronous
* @param configuration
*/
getCapabilities(configuration: Configuration): CapabilitiesResponse;
getCapabilities(configuration: Configuration): Capabilities;

/**
* Get the connector's schema.
Expand Down
4 changes: 3 additions & 1 deletion src/schema/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { JSONSchemaObject } from "@json-schema-tools/meta-schema";
import schema from "./schema.generated.json";
import { VERSION } from "./version.generated";

function schemaForType(type_name: string): JSONSchemaObject {
return {
Expand Down Expand Up @@ -29,5 +30,6 @@ export {
MutationRequestSchema,
MutationResponseSchema,
ErrorResponseSchema,
ValidateResponseSchema
ValidateResponseSchema,
VERSION,
};
1 change: 1 addition & 0 deletions src/schema/version.generated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const VERSION = "0.1.5";
19 changes: 14 additions & 5 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import Fastify, { FastifyRequest } from "fastify";
import opentelemetry, {
Attributes,
Span,
SpanStatusCode,
} from "@opentelemetry/api";

Expand All @@ -23,6 +21,7 @@ import {
MutationResponse,
MutationRequest,
QueryRequest,
VERSION
} from "./schema";

import { Options as AjvOptions } from "ajv";
Expand Down Expand Up @@ -98,6 +97,11 @@ export async function startServer<Configuration, State>(
);

server.addHook("preHandler", (request, reply, done) => {
// Don't apply authorization to the healthcheck endpoint
if (request.routeOptions.method === "GET" && request.routeOptions.url === "/health") {
return done();
}

const expectedAuthHeader =
options.serviceTokenSecret === undefined
? undefined
Expand Down Expand Up @@ -131,13 +135,18 @@ export async function startServer<Configuration, State>(
return withActiveSpan(
tracer,
"getCapabilities",
() => connector.getCapabilities(configuration)
() => ({
version: VERSION,
capabilities: connector.getCapabilities(configuration),
})
);
}
);

server.get("/health", (_request): Promise<undefined> => {
return connector.healthCheck(configuration, state);
server.get("/health", async (_request): Promise<undefined> => {
return connector.getHealthReadiness
? await connector.getHealthReadiness(configuration, state)
: undefined;
});

server.get("/metrics", (_request) => {
Expand Down
2 changes: 1 addition & 1 deletion typegen/regenerate-schema.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -eu -o pipefail

# Generate JSON schema from Rust types
cargo run -- ./src/schema/schema.generated.json
cargo run -- ./src/schema

# Generate TypeScript types from JSON schema
json2ts -i ./src/schema/schema.generated.json -o ./src/schema/schema.generated.ts --no-additionalProperties
Expand Down
32 changes: 23 additions & 9 deletions typegen/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use ndc_models::{
CapabilitiesResponse, ErrorResponse, ExplainResponse, MutationRequest, MutationResponse,
QueryRequest, QueryResponse, SchemaResponse,
QueryRequest, QueryResponse, SchemaResponse, VERSION,
};
use schemars::{schema_for, JsonSchema};
use serde_derive::{Deserialize, Serialize};
use std::{error::Error, fs, env};
use std::{env, error::Error, fs, path::PathBuf};

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
#[schemars(title = "SchemaRoot")]
Expand All @@ -30,20 +30,34 @@ struct ValidateResponse {

fn main() -> Result<(), Box<dyn Error>> {
let args = env::args().collect::<Vec<_>>();
let path = if args.len() >= 2 {
Ok(&args[1])
let (schema_json_path, version_path) = if args.len() >= 2 {
let schema_json_path = [args[1].as_str(), "schema.generated.json"]
.iter()
.collect::<PathBuf>();
let version_path = [args[1].as_str(), "version.generated.ts"]
.iter()
.collect::<PathBuf>();
Ok((schema_json_path, version_path))
} else {
Err("Schema file path not passed on command line")
Err("Schema directory not passed on command line")
}?;
print!("Generating Schema to {path}...");

print!(
"Generating schema JSON to {}...",
schema_json_path.to_str().unwrap()
);
let schema_json = serde_json::to_string_pretty(&schema_for!(SchemaRoot))?;
fs::write(schema_json_path, schema_json + "\n")?;
println!("done!");

print!(
"Generating schema version to {}...",
version_path.to_str().unwrap()
);
fs::write(
path,
schema_json + "\n",
version_path,
format!("export const VERSION = \"{VERSION}\";\n"),
)?;

println!("done!");

Ok(())
Expand Down

0 comments on commit 40448c6

Please sign in to comment.