Skip to content

Commit

Permalink
Add Spectral version 2
Browse files Browse the repository at this point in the history
  • Loading branch information
adriancarriger committed May 3, 2021
1 parent 7258e39 commit 0068c71
Show file tree
Hide file tree
Showing 18 changed files with 516 additions and 93 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@prismatic-io/spectral",
"version": "1.0.7",
"version": "2.0.0-beta.0",
"description": "Utility library for building Prismatic components",
"keywords": [
"prismatic"
Expand Down
67 changes: 60 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,69 @@
import {
ComponentDefinition,
ActionDefinition,
InputFieldDefinition,
PerformReturn,
Inputs,
PerformBranchingDataReturn,
PerformDataReturn,
} from "./types";
import {
ActionDefinition as ActionDefinitionV1,
ComponentDefinition,
PerformDataStructureReturn,
PerformBranchingDataStructureReturn,
} from "./server-types";

const convertAction = (
action: ActionDefinition<
Inputs,
boolean,
void | PerformBranchingDataReturn<unknown> | PerformDataReturn<unknown>
>
): ActionDefinitionV1 => {
const items = Object.entries(action.inputs);

export const component = (definition: ComponentDefinition) => definition;
export const action = (
definition: ActionDefinition
): Record<string, ActionDefinition> => ({
[definition.key]: definition,
const inputDefinitions = items.map(([key, value]) => ({
key,
...(typeof value === "object" ? value : {}),
})) as ActionDefinitionV1["inputs"];

return {
...action,
inputs: inputDefinitions,
perform: action.perform as ActionDefinitionV1["perform"],
examplePayload: action.examplePayload as
| PerformDataStructureReturn
| PerformBranchingDataStructureReturn,
};
};

export const component = (
definition: Omit<ComponentDefinition, "actions"> & {
actions: Record<
string,
ActionDefinition<any, boolean, PerformReturn<boolean, any>>
>;
}
) => ({
...definition,
actions: Object.fromEntries(
Object.entries(definition.actions).map(([actionKey, action]) => [
actionKey,
convertAction(action),
])
),
});
export const input = (definition: InputFieldDefinition) => definition;

export const action = <
T extends Inputs,
AllowsBranching extends boolean,
ReturnData extends PerformReturn<AllowsBranching, unknown>
>(
action: ActionDefinition<T, AllowsBranching, ReturnData>
) => action;

export const input = <T extends InputFieldDefinition>(definition: T) =>
definition;

export { default as util } from "./util";
export * from "./types";
Expand Down
88 changes: 30 additions & 58 deletions src/types.ts → src/server-types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* This is the shape that is actually sent on publish
*/

/** Defines attributes of a Component. */
export interface ComponentDefinition {
/** Specifies unique key for this Component. */
Expand All @@ -16,10 +20,8 @@ export interface ComponentDefinition {
documentationUrl?: string;
}

export type ConfigurationVariablesCollection = Record<string, string>;

/** Authorization settings for a component */
interface AuthorizationDefinition {
export interface AuthorizationDefinition {
/** Whether authorization is required */
required: boolean;
/** Supported authorization methods */
Expand All @@ -35,54 +37,46 @@ const authorizationMethods = [
"oauth2_client_credentials",
] as const;

export type AuthorizationMethod = typeof authorizationMethods[number];

export const AvailableAuthorizationMethods: AuthorizationMethod[] = [
...authorizationMethods,
];
type AuthorizationMethod = typeof authorizationMethods[number];

const oauth2AuthorizationMethods = [
"oauth2",
"oauth2_client_credentials",
] as const;

export type OAuth2AuthorizationMethod = typeof oauth2AuthorizationMethods[number];
type OAuth2AuthorizationMethod = typeof oauth2AuthorizationMethods[number];

export const AvailableOAuth2AuthorizationMethods: OAuth2AuthorizationMethod[] = [
...oauth2AuthorizationMethods,
];

export interface BasicCredential {
interface BasicCredential {
authorizationMethod: "basic";
fields: {
username: string;
password: string;
};
}

export interface ApiKeyCredential {
interface ApiKeyCredential {
authorizationMethod: "api_key";
fields: {
api_key: string;
};
}

export interface ApiKeySecretCredential {
interface ApiKeySecretCredential {
authorizationMethod: "api_key_secret";
fields: {
api_key: string;
api_secret: string;
};
}

export interface PrivateKeyCredential {
interface PrivateKeyCredential {
authorizationMethod: "private_key";
fields: {
username: string;
private_key: string;
};
}
export interface OAuth2Credential {
interface OAuth2Credential {
authorizationMethod: OAuth2AuthorizationMethod;
redirectUri: string;
fields: {
Expand All @@ -102,7 +96,7 @@ export interface OAuth2Credential {
context: { [key: string]: string };
}

export type Credential =
type Credential =
| BasicCredential
| ApiKeyCredential
| ApiKeySecretCredential
Expand All @@ -118,25 +112,11 @@ interface DisplayDefinition {
}

/** Component extensions for display properties. */
interface ComponentDisplayDefinition extends DisplayDefinition {
export interface ComponentDisplayDefinition extends DisplayDefinition {
/** Path to icon to use for this Component. Path should be relative to component roto index. */
iconPath?: string;
}

/** Request specification. */
export interface HttpRequestConfiguration {
/** Method of the HTTP request. */
method: "GET" | "PUT" | "POST" | "PATCH" | "DELETE" | "HEAD";
/** URL to send the HTTP request to. */
url: string;
/** Body of the request. */
body: unknown;
/** Parameters to send with the request. */
params: Record<string, string>;
/** Headers to send with the request. */
headers: Record<string, string>;
}

/** Configuration of an Action. */
export interface ActionDefinition {
/** Key used for the Actions map and to uniquely identify this Component in your tenant. */
Expand All @@ -162,16 +142,16 @@ export interface ActionDefinition {
}

/** Action-specific Display attributes. */
export interface ActionDisplayDefinition extends DisplayDefinition {
interface ActionDisplayDefinition extends DisplayDefinition {
/** Directions to help guide the user if additional configuration is required for this Action. */
directions?: string;
/** Indicate that this Action is important and/or commonly used from the parent Component. Should be enabled sparingly. */
important?: boolean;
}

export type ActionLoggerFunction = (...args: unknown[]) => void;
type ActionLoggerFunction = (...args: unknown[]) => void;

export interface ActionLogger {
interface ActionLogger {
debug: ActionLoggerFunction;
info: ActionLoggerFunction;
log: ActionLoggerFunction;
Expand All @@ -180,7 +160,7 @@ export interface ActionLogger {
}

/** Context provided to perform method containing helpers and contextual data */
export interface ActionContext {
interface ActionContext {
/** Credential for the action, optional since not all actions will require a credential */
credential?: Credential;
/** Logger for permanent logging; console calls are also captured */
Expand All @@ -192,7 +172,7 @@ export interface ActionContext {
}

/** Collection of input parameters provided by the user or previous steps' outputs */
export interface ActionInputParameters {
interface ActionInputParameters {
[key: string]: any;
}

Expand All @@ -215,7 +195,7 @@ export interface PerformDataStructureReturn {
}

/** Used to represent a binary or serialized data return as content type must be specified */
export interface PerformDataReturn {
interface PerformDataReturn {
/** Data payload containing data of the specified contentType */
data: Buffer | string | unknown;
/** The Content Type of the payload data */
Expand All @@ -234,31 +214,31 @@ export interface PerformBranchingDataStructureReturn
}

/** Used to represent a binary or serialized data branching return as content type must be specified */
export interface PerformBranchingDataReturn extends PerformDataReturn {
interface PerformBranchingDataReturn extends PerformDataReturn {
/** Name of the Branch to take. */
branch: string;
}

/** Required return type of all action perform functions */
export type PerformReturn =
type PerformReturn =
| PerformDataStructureReturn
| PerformBranchingDataStructureReturn
| PerformDataReturn
| PerformBranchingDataReturn
| void; // Allow an action to return nothing to reduce component implementation boilerplate

/** Definition of the function to perform when an Action is invoked. */
export type ActionPerformFunction = (
type ActionPerformFunction = (
context: ActionContext,
params: ActionInputParameters
) => Promise<PerformReturn>;

export type InputFieldDefinition =
type InputFieldDefinition =
| DefaultInputFieldDefinition
| CodeInputFieldDefinition;

/** Defines attributes of a InputField. */
export interface DefaultInputFieldDefinition {
interface DefaultInputFieldDefinition {
/** Unique identifier of the InputField. Must be unique within an Action. */
key: string;
/** Interface label of the InputField. */
Expand All @@ -281,13 +261,13 @@ export interface DefaultInputFieldDefinition {
model?: InputFieldChoice[] | InputFieldModelFunction;
}

export interface CodeInputFieldDefinition extends DefaultInputFieldDefinition {
interface CodeInputFieldDefinition extends DefaultInputFieldDefinition {
type: "code";
language?: string;
}

/** InputField type enumeration. */
export type InputFieldType =
type InputFieldType =
| "string"
| "text"
| "password"
Expand All @@ -297,15 +277,7 @@ export type InputFieldType =
| "conditional";

/** InputField collection enumeration */
export type InputFieldCollection = "valuelist" | "keyvaluelist";

/** KeyValuePair input parameter type */
export interface KeyValuePair<V = unknown> {
/** Key of the KeyValuePair */
key: string;
/** Value of the KeyValuePair */
value: V;
}
type InputFieldCollection = "valuelist" | "keyvaluelist";

/** Binary data payload */
export interface DataPayload {
Expand All @@ -318,7 +290,7 @@ export interface DataPayload {
}

/** Defines a single Choice option for a InputField. */
export interface InputFieldChoice {
interface InputFieldChoice {
/** Label to display for this Choice. */
label: string;
/** Value to use if this Choice is chosen. */
Expand All @@ -327,4 +299,4 @@ export interface InputFieldChoice {

// TODO: Does this need to take in arguments? What would they be?
/** Definition of the function that returns an array of choices. */
export type InputFieldModelFunction = () => Promise<InputFieldChoice[]>;
type InputFieldModelFunction = () => Promise<InputFieldChoice[]>;
35 changes: 22 additions & 13 deletions src/testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
PerformReturn,
AuthorizationMethod,
AvailableAuthorizationMethods,
ActionPerformFunction,
Inputs,
} from "./types";
import { spyOn } from "jest-mock";

Expand Down Expand Up @@ -95,31 +95,40 @@ export const loggerMock = (): ActionLogger => ({
error: (spyOn(console, "error") as unknown) as ActionLoggerFunction,
});

export interface InvokeResult<TReturn extends PerformReturn> {
result: TReturn;
loggerMock: ActionLogger;
}

/** Invokes specified ActionDefinition perform function using supplied params
* and optional context. Accepts a generic type matching PerformReturn as a convenience
* to avoid extra casting within test methods. Returns an InvokeResult containing both the
* action result and a mock logger for asserting logging.
*/
export const invoke = async <TReturn extends PerformReturn>(
action: ActionDefinition | Record<string, ActionDefinition>,
params: ActionInputParameters,
export const invoke = async <
T extends Inputs,
AllowsBranching extends boolean,
ReturnData extends PerformReturn<AllowsBranching, unknown>
>(
actionBase:
| ActionDefinition<T, AllowsBranching, ReturnData>
| Record<string, ActionDefinition<T, AllowsBranching, ReturnData>>,
params: ActionInputParameters<T>,
context?: Partial<ActionContext>
): Promise<InvokeResult<TReturn>> => {
const perform = (action.perform ||
Object.values(action)[0].perform) as ActionPerformFunction;
) => {
const action = (actionBase.perform
? actionBase
: Object.values(actionBase)[0]) as ActionDefinition<
T,
AllowsBranching,
ReturnData
>;

const realizedContext = {
credential: undefined,
logger: loggerMock(),
instanceState: {},
stepId: "mockStepId",
...context,
};
const result = (await perform(realizedContext, params)) as TReturn;

const result = await action.perform(realizedContext, params);

return {
result,
loggerMock: realizedContext.logger,
Expand Down
Loading

0 comments on commit 0068c71

Please sign in to comment.