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
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const attributeSchemaType = z.object({
export const ProviderAttributesSchemaResponseSchema = z.object({
host: attributeSchemaType,
email: attributeSchemaType,
"discord-username": attributeSchemaType,
organization: attributeSchemaType,
website: attributeSchemaType,
tier: attributeSchemaType,
Expand All @@ -34,15 +35,18 @@ export const ProviderAttributesSchemaResponseSchema = z.object({
"hardware-cpu-arch": attributeSchemaType,
"hardware-gpu": attributeSchemaType,
"hardware-gpu-model": attributeSchemaType,
"hardware-disk": attributeSchemaType,
"hardware-gpu-capability": attributeSchemaType,
"hardware-persistent-storage-class": attributeSchemaType,
"hardware-persistent-storage-capability": attributeSchemaType,
"hardware-cuda": attributeSchemaType,
datacenter: attributeSchemaType,
"hardware-memory": attributeSchemaType,
"network-provider": attributeSchemaType,
"network-speed-up": attributeSchemaType,
"network-speed-down": attributeSchemaType,
"feat-persistent-storage": attributeSchemaType,
"feat-persistent-storage-type": attributeSchemaType,
"workload-support-chia": attributeSchemaType,
"workload-support-chia-capabilities": attributeSchemaType,
"feat-shm": attributeSchemaType,
"hardware-shm": attributeSchemaType,
"feat-endpoint-ip": attributeSchemaType,
"feat-endpoint-custom-domain": attributeSchemaType
});
Expand Down
22 changes: 14 additions & 8 deletions apps/api/src/provider/http-schemas/provider.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const ProviderListResponseSchema = z.array(
})
),
host: z.string().nullable(),
discordUsername: z.string().nullable(),
organization: z.string().nullable(),
statusPage: z.string().nullable(),
locationRegion: z.string().nullable(),
Expand All @@ -73,17 +74,19 @@ export const ProviderListResponseSchema = z.array(
hardwareCpuArch: z.string().nullable(),
hardwareGpuVendor: z.string().nullable(),
hardwareGpuModels: z.array(z.string()).nullable(),
hardwareDisk: z.array(z.string()).nullable(),
hardwareGpuCapabilities: z.array(z.string()).nullable(),
hardwarePersistentStorageClass: z.string().nullable(),
featPersistentStorage: z.boolean(),
featPersistentStorageType: z.array(z.string()).nullable(),
featShm: z.boolean(),
hardwareShm: z.array(z.string()).nullable(),
hardwareCuda: z.string().nullable(),
datacenter: z.string().nullable(),
hardwareMemory: z.string().nullable(),
networkProvider: z.string().nullable(),
networkSpeedDown: z.number(),
networkSpeedUp: z.number(),
tier: z.string().nullable(),
featEndpointCustomDomain: z.boolean(),
workloadSupportChia: z.boolean(),
workloadSupportChiaCapabilities: z.array(z.string()).nullable(),
featEndpointIp: z.boolean()
})
);
Expand Down Expand Up @@ -151,6 +154,7 @@ export const ProviderResponseSchema = z.object({
})
),
host: z.string().nullable(),
discordUsername: z.string().nullable(),
organization: z.string().nullable(),
statusPage: z.string().nullable(),
locationRegion: z.string().nullable(),
Expand All @@ -163,17 +167,19 @@ export const ProviderResponseSchema = z.object({
hardwareCpuArch: z.string().nullable(),
hardwareGpuVendor: z.string().nullable(),
hardwareGpuModels: z.array(z.string()),
hardwareDisk: z.array(z.string()),
hardwareGpuCapabilities: z.array(z.string()),
hardwarePersistentStorageClass: z.string().nullable(),
featPersistentStorage: z.boolean(),
featPersistentStorageType: z.array(z.string()),
featShm: z.boolean(),
hardwareShm: z.array(z.string()),
hardwareCuda: z.string().nullable(),
datacenter: z.string().nullable(),
hardwareMemory: z.string().nullable(),
networkProvider: z.string().nullable(),
networkSpeedDown: z.number(),
networkSpeedUp: z.number(),
tier: z.string().nullable(),
featEndpointCustomDomain: z.boolean(),
workloadSupportChia: z.boolean(),
workloadSupportChiaCapabilities: z.array(z.string()),
featEndpointIp: z.boolean(),
uptime: z.array(
z.object({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import "@test/setup-provider-attributes-schema";

import type { Provider } from "@akashnetwork/database/dbSchemas/akash";
import { ProviderAttribute } from "@akashnetwork/database/dbSchemas/akash";
import type { GitHubHttpService } from "@akashnetwork/http-sdk";
import fs from "fs/promises";
import path from "path";
import { container } from "tsyringe";

import { CHAIN_DB } from "@src/chain";
import { ProviderAttributesSchemaService } from "../provider-attributes-schema/provider-attributes-schema.service";
import { ProviderRegionsService } from "./provider-regions.service";

import { createProvider } from "@test/seeders";
Expand Down Expand Up @@ -38,22 +36,12 @@ describe("ProviderRegions", () => {

describe("GET /v1/provider-regions", () => {
it("returns providers grouped by regions", async () => {
const service = setup();
const service = container.resolve(ProviderRegionsService);
const data = Object.groupBy(await service.getProviderRegions(), item => item.key);

expect(data["na-ca-west"]?.[0]?.providers?.sort()).toEqual([providers[0].owner].sort());
expect(data["na-ca-central"]?.[0]?.providers?.sort()).toEqual([providers[1].owner, providers[2].owner].sort());
expect(data["na-ca-prairie"]?.[0]?.providers?.sort()).toEqual([]);
});
});

function setup() {
return new ProviderRegionsService(
new ProviderAttributesSchemaService({
async getProviderAttributesSchema() {
return JSON.parse(await fs.readFile(path.join(__dirname, "../../../../../../config/provider-attributes.json"), "utf8"));
}
} as unknown as GitHubHttpService)
);
}
});
33 changes: 2 additions & 31 deletions apps/api/src/provider/services/provider/provider.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { JwtTokenPayload } from "@akashnetwork/chain-sdk";
import type { Provider } from "@akashnetwork/database/dbSchemas/akash";
import type { ProviderAttributesSchema } from "@akashnetwork/http-sdk";
import { faker } from "@faker-js/faker";
import { AxiosError } from "axios";
import { Ok } from "ts-results";
Expand All @@ -11,6 +10,7 @@ import { cacheEngine } from "@src/caching/helpers";
import { AUDITOR } from "@src/deployment/config/provider.config";
import { createLeaseStatus } from "../../../../test/seeders/lease-status.seeder";
import { createProviderSeed, createProviderWithAttributeSignatures } from "../../../../test/seeders/provider.seeder";
import { createProviderAttributesSchema } from "../../../../test/seeders/provider-attributes-schema.seeder";
import { createUserWallet } from "../../../../test/seeders/user-wallet.seeder";
import type { ProviderRepository } from "../../repositories/provider/provider.repository";
import type { AuditorService } from "../auditors/auditors.service";
Expand All @@ -19,36 +19,7 @@ import type { ProviderJwtTokenService } from "../provider-jwt-token/provider-jwt
import { ProviderService } from "./provider.service";
import type { ProviderProxyService } from "./provider-proxy.service";

const schemaDetail = { key: "test", type: "string" as const, required: false, description: "test", values: null };
const providerAttributeSchemaStub: ProviderAttributesSchema = {
host: schemaDetail,
email: schemaDetail,
organization: schemaDetail,
website: schemaDetail,
tier: schemaDetail,
"status-page": schemaDetail,
"location-region": schemaDetail,
country: schemaDetail,
city: schemaDetail,
timezone: schemaDetail,
"location-type": schemaDetail,
"hosting-provider": schemaDetail,
"hardware-cpu": schemaDetail,
"hardware-cpu-arch": schemaDetail,
"hardware-gpu": schemaDetail,
"hardware-gpu-model": schemaDetail,
"hardware-disk": schemaDetail,
"hardware-memory": schemaDetail,
"network-provider": schemaDetail,
"network-speed-up": schemaDetail,
"network-speed-down": schemaDetail,
"feat-persistent-storage": schemaDetail,
"feat-persistent-storage-type": schemaDetail,
"workload-support-chia": schemaDetail,
"workload-support-chia-capabilities": schemaDetail,
"feat-endpoint-ip": schemaDetail,
"feat-endpoint-custom-domain": schemaDetail
};
const providerAttributeSchemaStub = createProviderAttributesSchema();

describe(ProviderService.name, () => {
describe("sendManifest", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,12 @@ export class ProviderService {
this.providerAttributesSchemaService.getProviderAttributesSchema()
]);

const mapped = mapProviderToList(provider, providerAttributeSchema, auditors, lastSuccessfulSnapshot ?? undefined);

return {
...mapProviderToList(provider, providerAttributeSchema, auditors, lastSuccessfulSnapshot ?? undefined),
...mapped,
hardwareGpuCapabilities: mapped.hardwareGpuCapabilities ?? [],
Comment thread
Zblocker64 marked this conversation as resolved.
hardwareShm: mapped.hardwareShm ?? [],
uptime: uptimeSnapshots.map(ps => ({
id: ps.id,
isOnline: ps.isOnline,
Expand Down
15 changes: 10 additions & 5 deletions apps/api/src/types/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface ProviderList {

// Attributes schema
host: string | null;
discordUsername: string | null;
organization: string | null;
statusPage: string | null;
locationRegion: string | null;
Expand All @@ -50,17 +51,19 @@ export interface ProviderList {
hardwareCpuArch: string | null;
hardwareGpuVendor: string | null;
hardwareGpuModels: string[] | null;
hardwareDisk: string[] | null;
hardwareGpuCapabilities: string[] | null;
hardwarePersistentStorageClass: string | null;
featPersistentStorage: boolean;
featPersistentStorageType: string[] | null;
featShm: boolean;
hardwareShm: string[] | null;
hardwareCuda: string | null;
datacenter: string | null;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
hardwareMemory: string | null;
networkProvider: string | null;
networkSpeedDown: number;
networkSpeedUp: number;
tier: string | null;
featEndpointCustomDomain: boolean;
workloadSupportChia: boolean;
workloadSupportChiaCapabilities: string[] | null;
featEndpointIp: boolean;
}

Expand All @@ -75,7 +78,9 @@ export interface ProviderCapacityStats {
};
}

export interface ProviderDetail extends ProviderList {
export interface ProviderDetail extends Omit<ProviderList, "hardwareGpuCapabilities" | "hardwareShm"> {
hardwareGpuCapabilities: string[];
hardwareShm: string[];
uptime: {
id: string;
isOnline: boolean;
Expand Down
121 changes: 121 additions & 0 deletions apps/api/src/utils/map/provider.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import type { Provider } from "@akashnetwork/database/dbSchemas/akash";
import { describe, expect, it } from "vitest";

import { createProviderSeed } from "../../../test/seeders/provider.seeder";
import { createProviderAttributesSchema } from "../../../test/seeders/provider-attributes-schema.seeder";
import { mapProviderToList } from "./provider";

const providerAttributeSchemaStub = createProviderAttributesSchema();

function mapWithAttributes(attributes: Array<{ key: string; value: string }>) {
const provider = {
...createProviderSeed({ isOnline: true, cosmosSdkVersion: "v0.45.9" }),
providerAttributes: attributes,
providerAttributeSignatures: []
} as Provider;

return mapProviderToList(provider, providerAttributeSchemaStub, [], undefined);
}

describe(mapProviderToList.name, () => {
describe("discordUsername", () => {
it("maps discord-username from on-chain attributes", () => {
const mapped = mapWithAttributes([{ key: "discord-username", value: "provider_contact" }]);

expect(mapped.discordUsername).toBe("provider_contact");
});

it("is null when discord-username is absent", () => {
const mapped = mapWithAttributes([{ key: "host", value: "example.com" }]);

expect(mapped.discordUsername).toBeNull();
});
});

describe("locationRegion", () => {
it("maps location-region from on-chain attributes", () => {
const mapped = mapWithAttributes([{ key: "location-region", value: "na-us-west" }]);

expect(mapped.locationRegion).toBe("na-us-west");
});

it("is null when only legacy region is present", () => {
const mapped = mapWithAttributes([{ key: "region", value: "us-west" }]);

expect(mapped.locationRegion).toBeNull();
});
});

describe("featShm", () => {
it("is true when feat-shm is true", () => {
const mapped = mapWithAttributes([{ key: "feat-shm", value: "true" }]);

expect(mapped.featShm).toBe(true);
});

it("is true when any storage class is ram", () => {
const mapped = mapWithAttributes([
{ key: "capabilities/storage/2/class", value: "ram" },
{ key: "capabilities/storage/2/persistent", value: "false" }
]);

expect(mapped.featShm).toBe(true);
});

it("is false when no shm signals are present", () => {
const mapped = mapWithAttributes([{ key: "capabilities/storage/1/class", value: "beta3" }]);

expect(mapped.featShm).toBe(false);
});
});

describe("hardwareShm", () => {
it("maps shm capability attributes from the schema", () => {
const mapped = mapWithAttributes([
{ key: "capabilities/storage/2/class", value: "ram" },
{ key: "capabilities/storage/2/persistent", value: "false" }
]);

expect(mapped.hardwareShm).toEqual(["SHM storage class", "SHM non-persistent"]);
});
});

describe("hardwareGpuCapabilities", () => {
it("maps gpu capability attributes from the schema", () => {
const mapped = mapWithAttributes([{ key: "capabilities/gpu/vendor/nvidia/model/rtx4090", value: "true" }]);

expect(mapped.hardwareGpuCapabilities).toEqual(["nvidia rtx4090 24Gi pcie"]);
});
});

describe("featPersistentStorage", () => {
it("is true when feat-persistent-storage is true", () => {
const mapped = mapWithAttributes([{ key: "feat-persistent-storage", value: "true" }]);

expect(mapped.featPersistentStorage).toBe(true);
});

it("is true when capabilities/storage/1/persistent is true", () => {
const mapped = mapWithAttributes([{ key: "capabilities/storage/1/persistent", value: "true" }]);

expect(mapped.featPersistentStorage).toBe(true);
});

it("is true when persistent storage is declared on a non-default storage index", () => {
const mapped = mapWithAttributes([
{ key: "capabilities/storage/1/class", value: "beta3" },
{ key: "capabilities/storage/1/persistent", value: "false" },
{ key: "capabilities/storage/2/class", value: "beta2" },
{ key: "capabilities/storage/2/persistent", value: "true" }
]);

expect(mapped.featPersistentStorage).toBe(true);
});

it("is false when no persistent storage signals are present", () => {
const mapped = mapWithAttributes([{ key: "capabilities/storage/1/persistent", value: "false" }]);

expect(mapped.featPersistentStorage).toBe(false);
});
});
});
Loading
Loading