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
83 changes: 83 additions & 0 deletions packages/base/command.gts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ export class SwitchSubmodeInput extends CardDef {
@field createFile = contains(BooleanField);
}

export class PersistModuleInspectorViewInput extends CardDef {
@field codePath = contains(StringField);
@field moduleInspectorView = contains(StringField); // 'schema' | 'spec' | 'preview'
}

export class SwitchSubmodeResult extends CardDef {
@field codePath = contains(StringField);
}
Expand Down Expand Up @@ -534,6 +539,84 @@ export class GetAllRealmMetasResult extends CardDef {
@field results = containsMany(RealmMetaField);
}

export class GetAvailableRealmUrlsResult extends CardDef {
@field urls = containsMany(StringField);
}

export class GetCatalogRealmUrlsResult extends CardDef {
@field urls = containsMany(StringField);
}

export class FetchCardJsonInput extends CardDef {
@field url = contains(StringField);
}

export class FetchCardJsonResult extends CardDef {
@field document = contains(JsonField);
}

export class ExecuteAtomicOperationsInput extends CardDef {
@field realmUrl = contains(StringField);
@field operations = containsMany(JsonField);
}

export class ExecuteAtomicOperationsResult extends CardDef {
@field results = containsMany(JsonField);
}

export class StoreAddInput extends CardDef {
@field document = contains(JsonField);
@field realm = contains(StringField);
}

export class GetRealmOfUrlInput extends CardDef {
@field url = contains(StringField);
}

export class GetRealmOfUrlResult extends CardDef {
@field realmUrl = contains(StringField); // empty string if not found
}

export class CanReadRealmInput extends CardDef {
@field realmUrl = contains(StringField);
}

export class CanReadRealmResult extends CardDef {
@field canRead = contains(BooleanField);
}

export class AuthedFetchInput extends CardDef {
@field url = contains(StringField);
@field method = contains(StringField);
@field acceptHeader = contains(StringField);
}

export class AuthedFetchResult extends CardDef {
@field ok = contains(BooleanField);
@field status = contains(NumberField);
@field body = contains(JsonField);
}

export class GetDefaultWritableRealmResult extends CardDef {
@field realmUrl = contains(StringField); // empty string if no writable realm found
}

export class ValidateRealmInput extends CardDef {
@field realmUrl = contains(StringField);
}

export class ValidateRealmResult extends CardDef {
@field realmUrl = contains(StringField); // normalized with trailing slash
}

export class SanitizeModuleListInput extends CardDef {
@field moduleUrls = containsMany(StringField);
}

export class SanitizeModuleListResult extends CardDef {
@field moduleUrls = containsMany(StringField);
}

export class SearchGoogleImagesInput extends CardDef {
@field query = contains(StringField);
@field maxResults = contains(NumberField); // optional, default 10
Expand Down
52 changes: 52 additions & 0 deletions packages/host/app/commands/authed-fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { service } from '@ember/service';

import type * as BaseCommandModule from 'https://cardstack.com/base/command';

import HostBaseCommand from '../lib/host-base-command';

import type NetworkService from '../services/network';

export default class AuthedFetchCommand extends HostBaseCommand<
typeof BaseCommandModule.AuthedFetchInput,
typeof BaseCommandModule.AuthedFetchResult
> {
@service declare private network: NetworkService;

description = 'Perform an authenticated HTTP fetch';

async getInputType() {
let commandModule = await this.loadCommandModule();
const { AuthedFetchInput } = commandModule;
return AuthedFetchInput;
}

requireInputFields = ['url'];

protected async run(
input: BaseCommandModule.AuthedFetchInput,
): Promise<BaseCommandModule.AuthedFetchResult> {
let commandModule = await this.loadCommandModule();
const { AuthedFetchResult } = commandModule;
const headers: Record<string, string> = {};
if (input.acceptHeader) {
headers['Accept'] = input.acceptHeader;
}
const response = await this.network.authedFetch(input.url, {
method: input.method ?? 'GET',
headers,
});
let body: Record<string, any> = {};
if (response.ok) {
try {
body = await response.json();
} catch {
// non-JSON response
}
}
return new AuthedFetchResult({
ok: response.ok,
status: response.status,
body,
});
}
}
34 changes: 34 additions & 0 deletions packages/host/app/commands/can-read-realm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { service } from '@ember/service';

import type * as BaseCommandModule from 'https://cardstack.com/base/command';

import HostBaseCommand from '../lib/host-base-command';

import type RealmService from '../services/realm';

export default class CanReadRealmCommand extends HostBaseCommand<
typeof BaseCommandModule.CanReadRealmInput,
typeof BaseCommandModule.CanReadRealmResult
> {
@service declare private realm: RealmService;

description = 'Check whether the current user can read a realm';

async getInputType() {
let commandModule = await this.loadCommandModule();
const { CanReadRealmInput } = commandModule;
return CanReadRealmInput;
}

requireInputFields = ['realmUrl'];

protected async run(
input: BaseCommandModule.CanReadRealmInput,
): Promise<BaseCommandModule.CanReadRealmResult> {
let commandModule = await this.loadCommandModule();
const { CanReadRealmResult } = commandModule;
return new CanReadRealmResult({
canRead: this.realm.canRead(input.realmUrl),
});
}
}
44 changes: 44 additions & 0 deletions packages/host/app/commands/execute-atomic-operations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { service } from '@ember/service';

import type { AtomicOperation } from '@cardstack/runtime-common/atomic-document';

import type * as BaseCommandModule from 'https://cardstack.com/base/command';

import HostBaseCommand from '../lib/host-base-command';

import type CardService from '../services/card-service';

export default class ExecuteAtomicOperationsCommand extends HostBaseCommand<
typeof BaseCommandModule.ExecuteAtomicOperationsInput,
typeof BaseCommandModule.ExecuteAtomicOperationsResult
> {
@service declare private cardService: CardService;

description = 'Execute atomic operations against a realm';

async getInputType() {
let commandModule = await this.loadCommandModule();
const { ExecuteAtomicOperationsInput } = commandModule;
return ExecuteAtomicOperationsInput;
}

requireInputFields = ['realmUrl', 'operations'];

protected async run(
input: BaseCommandModule.ExecuteAtomicOperationsInput,
): Promise<BaseCommandModule.ExecuteAtomicOperationsResult> {
let commandModule = await this.loadCommandModule();
const { ExecuteAtomicOperationsResult } = commandModule;
const results = await this.cardService.executeAtomicOperations(
input.operations as AtomicOperation[],
new URL(input.realmUrl),
);
const atomicResults = results['atomic:results'];
if (!Array.isArray(atomicResults)) {
const detail = (results as { errors?: Array<{ detail?: string }> })
.errors?.[0]?.detail;
throw new Error(detail ?? 'Atomic operations failed');
}
return new ExecuteAtomicOperationsResult({ results: atomicResults });
}
}
35 changes: 35 additions & 0 deletions packages/host/app/commands/fetch-card-json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { service } from '@ember/service';

import { cardIdToURL } from '@cardstack/runtime-common';

import type * as BaseCommandModule from 'https://cardstack.com/base/command';

import HostBaseCommand from '../lib/host-base-command';

import type CardService from '../services/card-service';

export default class FetchCardJsonCommand extends HostBaseCommand<
typeof BaseCommandModule.FetchCardJsonInput,
typeof BaseCommandModule.FetchCardJsonResult
> {
@service declare private cardService: CardService;

description = 'Fetch a card as a JSON document by URL';

async getInputType() {
let commandModule = await this.loadCommandModule();
const { FetchCardJsonInput } = commandModule;
return FetchCardJsonInput;
}

requireInputFields = ['url'];

protected async run(
input: BaseCommandModule.FetchCardJsonInput,
): Promise<BaseCommandModule.FetchCardJsonResult> {
let commandModule = await this.loadCommandModule();
const { FetchCardJsonResult } = commandModule;
const doc = await this.cardService.fetchJSON(cardIdToURL(input.url));
return new FetchCardJsonResult({ document: doc });
}
}
29 changes: 29 additions & 0 deletions packages/host/app/commands/get-available-realm-urls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { service } from '@ember/service';

import type * as BaseCommandModule from 'https://cardstack.com/base/command';

import HostBaseCommand from '../lib/host-base-command';

import type RealmServerService from '../services/realm-server';

export default class GetAvailableRealmUrlsCommand extends HostBaseCommand<
undefined,
typeof BaseCommandModule.GetAvailableRealmUrlsResult
> {
@service declare private realmServer: RealmServerService;

static actionVerb = 'Get Realm URLs';
description = 'Get the list of available realm URLs';

async getInputType() {
return undefined;
}

protected async run(): Promise<BaseCommandModule.GetAvailableRealmUrlsResult> {
let commandModule = await this.loadCommandModule();
const { GetAvailableRealmUrlsResult } = commandModule;
return new GetAvailableRealmUrlsResult({
urls: this.realmServer.availableRealmURLs,
});
}
}
29 changes: 29 additions & 0 deletions packages/host/app/commands/get-catalog-realm-urls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { service } from '@ember/service';

import type * as BaseCommandModule from 'https://cardstack.com/base/command';

import HostBaseCommand from '../lib/host-base-command';

import type RealmServerService from '../services/realm-server';

export default class GetCatalogRealmUrlsCommand extends HostBaseCommand<
undefined,
typeof BaseCommandModule.GetCatalogRealmUrlsResult
> {
@service declare private realmServer: RealmServerService;

static actionVerb = 'Get Catalog Realm URLs';
description = 'Get the list of catalog realm URLs';

async getInputType() {
return undefined;
}

protected async run(): Promise<BaseCommandModule.GetCatalogRealmUrlsResult> {
let commandModule = await this.loadCommandModule();
const { GetCatalogRealmUrlsResult } = commandModule;
return new GetCatalogRealmUrlsResult({
urls: this.realmServer.catalogRealmURLs,
});
}
}
28 changes: 28 additions & 0 deletions packages/host/app/commands/get-default-writable-realm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { service } from '@ember/service';

import type * as BaseCommandModule from 'https://cardstack.com/base/command';

import HostBaseCommand from '../lib/host-base-command';

import type RealmService from '../services/realm';

export default class GetDefaultWritableRealmCommand extends HostBaseCommand<
undefined,
typeof BaseCommandModule.GetDefaultWritableRealmResult
> {
@service declare private realm: RealmService;

description = 'Get the path of the default writable realm';

async getInputType() {
return undefined;
}

protected async run(): Promise<BaseCommandModule.GetDefaultWritableRealmResult> {
let commandModule = await this.loadCommandModule();
const { GetDefaultWritableRealmResult } = commandModule;
return new GetDefaultWritableRealmResult({
realmUrl: this.realm.defaultWritableRealm?.path ?? '',
});
}
}
33 changes: 33 additions & 0 deletions packages/host/app/commands/get-realm-of-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { service } from '@ember/service';

import type * as BaseCommandModule from 'https://cardstack.com/base/command';

import HostBaseCommand from '../lib/host-base-command';

import type RealmService from '../services/realm';

export default class GetRealmOfUrlCommand extends HostBaseCommand<
typeof BaseCommandModule.GetRealmOfUrlInput,
typeof BaseCommandModule.GetRealmOfUrlResult
> {
@service declare private realm: RealmService;

description = 'Get the realm URL that contains a given URL';

async getInputType() {
let commandModule = await this.loadCommandModule();
const { GetRealmOfUrlInput } = commandModule;
return GetRealmOfUrlInput;
}

requireInputFields = ['url'];

protected async run(
input: BaseCommandModule.GetRealmOfUrlInput,
): Promise<BaseCommandModule.GetRealmOfUrlResult> {
let commandModule = await this.loadCommandModule();
const { GetRealmOfUrlResult } = commandModule;
const realmUrl = this.realm.realmOfURL(new URL(input.url));
return new GetRealmOfUrlResult({ realmUrl: realmUrl?.href ?? '' });
}
}
Loading
Loading