Skip to content

Commit

Permalink
feat: mark user owned probes in API responses (#607)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexey-yarmosh authored and MartinKolarik committed Jan 28, 2025
1 parent 4612703 commit b6fe2d4
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 36 deletions.
11 changes: 6 additions & 5 deletions migrations/create-tables.js.sql
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
CREATE TABLE IF NOT EXISTS directus_users (
id CHAR(36) PRIMARY KEY,
github_username VARCHAR(255),
user_type VARCHAR(255) NOT NULL DEFAULT 'member'
user_type VARCHAR(255) NOT NULL DEFAULT 'member',
public_probes BOOLEAN DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS gp_adopted_probes (
Expand Down Expand Up @@ -34,15 +35,15 @@ CREATE TABLE IF NOT EXISTS gp_adopted_probes (
asn INTEGER NOT NULL,
network VARCHAR(255) NOT NULL,
countryOfCustomCity VARCHAR(255)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS directus_notifications (
id CHAR(10),
recipient CHAR(36),
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
subject VARCHAR(255),
message TEXT
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `gp_tokens` (
`date_created` timestamp NULL DEFAULT NULL,
Expand Down Expand Up @@ -73,7 +74,7 @@ CREATE TABLE IF NOT EXISTS gp_credits (
user_id VARCHAR(36) NOT NULL,
CONSTRAINT gp_credits_user_id_unique UNIQUE (user_id),
CONSTRAINT gp_credits_amount_positive CHECK (`amount` >= 0)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS gp_location_overrides (
id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
Expand All @@ -87,4 +88,4 @@ CREATE TABLE IF NOT EXISTS gp_location_overrides (
country VARCHAR(255),
latitude FLOAT(10, 5),
longitude FLOAT(10, 5)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
31 changes: 17 additions & 14 deletions src/lib/override/adopted-probes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const logger = scopedLogger('adopted-probes');

export const ADOPTIONS_TABLE = 'gp_adopted_probes';
export const NOTIFICATIONS_TABLE = 'directus_notifications';
export const USERS_TABLE = 'directus_users';

export type Adoption = {
id: string;
Expand All @@ -29,8 +30,8 @@ export type Adoption = {
systemTags: string[];
isCustomCity: boolean;
status: string;
isIPv4Supported: boolean,
isIPv6Supported: boolean,
isIPv4Supported: boolean;
isIPv6Supported: boolean;
version: string | null;
nodeVersion: string | null;
hardwareDevice: string | null;
Expand All @@ -43,15 +44,18 @@ export type Adoption = {
longitude: number | null;
asn: number | null;
network: string | null;
githubUsername: string | null;
publicProbes: boolean;
}

type Row = Omit<Adoption, 'isCustomCity' | 'tags' | 'systemTags' | 'altIps' | 'isIPv4Supported' | 'isIPv6Supported'> & {
export type Row = Omit<Adoption, 'isCustomCity' | 'tags' | 'systemTags' | 'altIps' | 'isIPv4Supported' | 'isIPv6Supported' | 'publicProbes'> & {
altIps: string;
tags: string;
systemTags: string;
isCustomCity: number;
isIPv4Supported: number;
isIPv6Supported: number;
publicProbes: number;
}

type AdoptionFieldDescription = {
Expand Down Expand Up @@ -166,16 +170,20 @@ export class AdoptedProbes {
};
}

getUpdatedTags (probe: Probe) {
getUpdatedTags (probe: Probe): Tag[] {
const adoption = this.getByIp(probe.ipAddress);

if (!adoption || !adoption.tags.length) {
if (!adoption || (!adoption.tags.length && !adoption.publicProbes)) {
return probe.tags;
}

return [
...probe.tags,
...adoption.tags,
...(adoption.publicProbes && adoption.githubUsername ? [{
type: 'user' as const,
value: `u-${adoption.githubUsername}`,
}] : []),
];
}

Expand All @@ -187,13 +195,6 @@ export class AdoptedProbes {
return probe;
}

const isCustomCity = adoption.isCustomCity;
const hasUserTags = adoption.tags && adoption.tags.length;

if (!isCustomCity && !hasUserTags) {
return { ...probe, owner: { id: adoption.userId } };
}

const newLocation = this.getUpdatedLocation(probe.ipAddress, probe.location) || probe.location;

const newTags = this.getUpdatedTags(probe);
Expand Down Expand Up @@ -242,10 +243,11 @@ export class AdoptedProbes {

public async fetchAdoptions () {
const rows = await this.sql(ADOPTIONS_TABLE)
.leftJoin(USERS_TABLE, `${ADOPTIONS_TABLE}.userId`, `${USERS_TABLE}.id`)
// First item will be preserved, so we are prioritizing online probes.
// Sorting by id at the end so order is the same in any table state.
.orderByRaw(`lastSyncDate DESC, onlineTimesToday DESC, FIELD(status, 'ready') DESC, id DESC`)
.select<Row[]>();
.orderByRaw(`${ADOPTIONS_TABLE}.lastSyncDate DESC, ${ADOPTIONS_TABLE}.onlineTimesToday DESC, FIELD(${ADOPTIONS_TABLE}.status, 'ready') DESC, ${ADOPTIONS_TABLE}.id DESC`)
.select<Row[]>(`${ADOPTIONS_TABLE}.*`, `${USERS_TABLE}.github_username AS githubUsername`, `${USERS_TABLE}.public_probes as publicProbes`);

const adoptions: Adoption[] = rows.map(row => ({
...row,
Expand All @@ -258,6 +260,7 @@ export class AdoptedProbes {
isIPv6Supported: Boolean(row.isIPv6Supported),
latitude: row.latitude ? normalizeCoordinate(row.latitude) : row.latitude,
longitude: row.longitude ? normalizeCoordinate(row.longitude) : row.longitude,
publicProbes: Boolean(row.publicProbes),
}));

this.adoptions = adoptions;
Expand Down
46 changes: 29 additions & 17 deletions test/tests/unit/override/adopted-probes.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { expect } from 'chai';
import * as sinon from 'sinon';
import relativeDayUtc from 'relative-day-utc';
import { AdoptedProbes } from '../../../../src/lib/override/adopted-probes.js';
import { AdoptedProbes, Row } from '../../../../src/lib/override/adopted-probes.js';
import type { Probe } from '../../../../src/probe/types.js';

describe('AdoptedProbes', () => {
const defaultAdoptedProbe = {
const defaultAdoptedProbe: Row = {
id: 'p-1',
name: 'probe-1',
userId: '3cff97ae-4a0a-4f34-9f1a-155e6def0a45',
username: 'jimaek',
ip: '1.1.1.1',
altIps: '[]',
uuid: '1-1-1-1-1',
Expand All @@ -31,6 +31,8 @@ describe('AdoptedProbes', () => {
longitude: -6.25,
asn: 16509,
network: 'Amazon.com, Inc.',
githubUsername: 'jimaek',
publicProbes: 0,
};

const defaultConnectedProbe: Probe = {
Expand Down Expand Up @@ -89,6 +91,7 @@ describe('AdoptedProbes', () => {
where: sandbox.stub(),
orWhere: sandbox.stub(),
whereIn: sandbox.stub(),
leftJoin: sandbox.stub(),
orderByRaw: sandbox.stub(),
} as any;
const sqlStub = sandbox.stub() as any;
Expand All @@ -104,6 +107,7 @@ describe('AdoptedProbes', () => {
sql.where.returns(sql);
sql.orWhere.returns(sql);
sql.whereIn.returns(sql);
sql.leftJoin.returns(sql);
sql.orderByRaw.returns(sql);
sql.select.resolves([ defaultAdoptedProbe ]);
sqlStub.returns(sql);
Expand Down Expand Up @@ -420,13 +424,13 @@ describe('AdoptedProbes', () => {

expect(sql.raw.args[0]![1]).to.deep.equal({
recipient: '3cff97ae-4a0a-4f34-9f1a-155e6def0a45',
subject: `Your probe's location has changed`,
message: 'Globalping detected that your [probe with IP address **1.1.1.1**](/probes/p-1) has changed its location from Ireland to United Kingdom. The custom city value "Dublin" is not applied anymore.\n\nIf this change is not right, please report it in [this issue](https://github.com/jsdelivr/globalping/issues/268).',
subject: 'Your probe\'s location has changed',
message: 'Globalping detected that your probe [**probe-1**](/probes/p-1) with IP address **1.1.1.1** has changed its location from Ireland to United Kingdom. The custom city value "Dublin" is not applied anymore.\n\nIf this change is not right, please report it in [this issue](https://github.com/jsdelivr/globalping/issues/268).',
});

expect(sql.raw.args[1]![1]).to.deep.equal({
recipient: '3cff97ae-4a0a-4f34-9f1a-155e6def0a45',
subject: `Your probe's location has changed`,
subject: 'Your probe\'s location has changed',
message: 'Globalping detected that your probe [**probe-gb-london-01**](/probes/p-9) with IP address **9.9.9.9** has changed its location from Ireland to United Kingdom. The custom city value "Dublin" is not applied anymore.\n\nIf this change is not right, please report it in [this issue](https://github.com/jsdelivr/globalping/issues/268).',
});

Expand Down Expand Up @@ -524,8 +528,8 @@ describe('AdoptedProbes', () => {

expect(sql.raw.args[2]![1]).to.deep.equal({
recipient: '3cff97ae-4a0a-4f34-9f1a-155e6def0a45',
subject: `Your probe's location has changed back`,
message: 'Globalping detected that your [probe with IP address **1.1.1.1**](/probes/p-1) has changed its location back from United Kingdom to Ireland. The custom city value "Dublin" is now applied again.',
subject: 'Your probe\'s location has changed back',
message: 'Globalping detected that your probe [**probe-1**](/probes/p-1) with IP address **1.1.1.1** has changed its location back from United Kingdom to Ireland. The custom city value "Dublin" is now applied again.',
});

expect(sql.raw.args[3]![1]).to.deep.equal({
Expand Down Expand Up @@ -770,11 +774,11 @@ describe('AdoptedProbes', () => {
isCustomCity: false,
isIPv4Supported: true,
isIPv6Supported: true,
publicProbes: false,
});
});

it('getUpdatedLocation method should return updated location', async () => {
delete process.env['SHOULD_SYNC_ADOPTIONS'];
const adoptedProbes = new AdoptedProbes(sqlStub, getProbesWithAdminData);
sql.select.resolves([{
...defaultAdoptedProbe,
Expand Down Expand Up @@ -803,7 +807,6 @@ describe('AdoptedProbes', () => {
});

it('getUpdatedLocation method should return null if connected.country !== adopted.countryOfCustomCity', async () => {
delete process.env['SHOULD_SYNC_ADOPTIONS'];
const adoptedProbes = new AdoptedProbes(sqlStub, getProbesWithAdminData);
sql.select.resolves([{
...defaultAdoptedProbe,
Expand All @@ -821,7 +824,6 @@ describe('AdoptedProbes', () => {
});

it('getUpdatedLocation method should return null if "isCustomCity: false"', async () => {
delete process.env['SHOULD_SYNC_ADOPTIONS'];
const adoptedProbes = new AdoptedProbes(sqlStub, getProbesWithAdminData);
sql.select.resolves([{
...defaultAdoptedProbe,
Expand All @@ -836,8 +838,16 @@ describe('AdoptedProbes', () => {
expect(updatedLocation).to.equal(null);
});

it('getUpdatedTags method should return updated tags', async () => {
delete process.env['SHOULD_SYNC_ADOPTIONS'];
it('getUpdatedTags method should return same tags array', async () => {
const adoptedProbes = new AdoptedProbes(sqlStub, getProbesWithAdminData);
sql.select.resolves([{ ...defaultAdoptedProbe, tags: '[]' }]);

await adoptedProbes.syncDashboardData();
const updatedTags = adoptedProbes.getUpdatedTags(defaultConnectedProbe);
expect(updatedTags).to.equal(defaultConnectedProbe.tags);
});

it('getUpdatedTags method should return user tags', async () => {
const adoptedProbes = new AdoptedProbes(sqlStub, getProbesWithAdminData);

await adoptedProbes.syncDashboardData();
Expand All @@ -848,13 +858,15 @@ describe('AdoptedProbes', () => {
]);
});

it('getUpdatedTags method should return same tags array if user tags are empty', async () => {
delete process.env['SHOULD_SYNC_ADOPTIONS'];
it('getUpdatedTags method should include user tag if public_probes: true', async () => {
const adoptedProbes = new AdoptedProbes(sqlStub, getProbesWithAdminData);
sql.select.resolves([{ ...defaultAdoptedProbe, tags: '[]' }]);
sql.select.resolves([{ ...defaultAdoptedProbe, tags: '[]', publicProbes: 1 }]);

await adoptedProbes.syncDashboardData();
const updatedTags = adoptedProbes.getUpdatedTags(defaultConnectedProbe);
expect(updatedTags).to.equal(defaultConnectedProbe.tags);
expect(updatedTags).to.deep.equal([
{ type: 'system', value: 'datacenter-network' },
{ type: 'user', value: 'u-jimaek' },
]);
});
});

0 comments on commit b6fe2d4

Please sign in to comment.