Skip to content

Commit

Permalink
Add Jira issues to the EmberData store (#652)
Browse files Browse the repository at this point in the history
* Add EmberData support to JiraIssues

* Remove`fade-in` animation

* Update types and descriptions
  • Loading branch information
jeffdaley authored Mar 18, 2024
1 parent 012d986 commit 6f50b29
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 38 deletions.
20 changes: 20 additions & 0 deletions web/app/adapters/jira-issue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import DS from "ember-data";
import ApplicationAdapter from "./application";
import RSVP from "rsvp";
import ModelRegistry from "ember-data/types/registries/model";
import JiraIssueModel from "hermes/models/jira-issue";

export default class JiraIssueAdapter extends ApplicationAdapter {
findRecord<K extends string | number>(
_store: DS.Store,
_type: ModelRegistry[K],
id: string,
_snapshot: DS.Snapshot<K>,
): RSVP.Promise<JiraIssueModel> {
const issue = this.fetchSvc
.fetch(`/api/${this.configSvc.config.api_version}/jira/issues/${id}`)
.then((response) => response?.json());

return RSVP.resolve(issue);
}
}
7 changes: 4 additions & 3 deletions web/app/components/project/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { Resize } from "ember-animated/motions/resize";
import { easeOutExpo, easeOutQuad } from "hermes/utils/ember-animated/easings";
import animateTransform from "hermes/utils/ember-animated/animate-transform";
import RouterService from "@ember/routing/router-service";
import StoreService from "hermes/services/store";

const animationDuration = Ember.testing ? 0 : 450;

Expand Down Expand Up @@ -58,6 +59,7 @@ export default class ProjectIndexComponent extends Component<ProjectIndexCompone
@service("config") declare configSvc: ConfigService;
@service declare flashMessages: HermesFlashMessagesService;
@service declare router: RouterService;
@service declare store: StoreService;

/**
* The array of possible project statuses.
Expand Down Expand Up @@ -602,9 +604,8 @@ export default class ProjectIndexComponent extends Component<ProjectIndexCompone
*/
loadJiraIssue = task(async (jiraIssueID?: string) => {
const id = jiraIssueID ?? this.args.project.jiraIssueID;
const issue = await this.fetchSvc
.fetch(`/api/${this.configSvc.config.api_version}/jira/issues/${id}`)
.then((response) => response?.json());
assert("jiraIssueID must exist", id);
const issue = await this.store.findRecord("jira-issue", id);
this.jiraIssue = issue;
});

Expand Down
5 changes: 3 additions & 2 deletions web/app/components/project/jira-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import { tracked } from "@glimmer/tracking";
import { inject as service } from "@ember/service";
import { restartableTask } from "ember-concurrency";
import FetchService from "hermes/services/fetch";
import { JiraIssue, JiraPickerResult } from "hermes/types/project";
import { JiraPickerResult } from "hermes/types/project";
import ConfigService from "hermes/services/config";
import { XDropdownListAnchorAPI } from "../x/dropdown-list";
import { next } from "@ember/runloop";
import { assert } from "@ember/debug";
import JiraIssueModel from "hermes/models/jira-issue";

interface ProjectJiraWidgetComponentSignature {
Element: HTMLDivElement;
Args: {
issue?: JiraPickerResult | JiraIssue;
issue?: JiraPickerResult | JiraIssueModel;
onIssueSelect?: (issue: any) => void;
onIssueRemove?: () => void;
isDisabled?: boolean;
Expand Down
3 changes: 1 addition & 2 deletions web/app/components/project/tile.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@
<ExternalLink
data-test-jira-link
href={{this.jiraIssue.url}}
class="jira inline-flex items-center gap-1.5
{{if this.jiraIssue 'fade-in-forwards'}}"
class="jira inline-flex items-center gap-1.5"
tabindex="-1"
>
<div class="flex h-4 w-4">
Expand Down
25 changes: 13 additions & 12 deletions web/app/components/project/tile.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { assert } from "@ember/debug";
import { inject as service } from "@ember/service";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { task } from "ember-concurrency";
import JiraIssueModel from "hermes/models/jira-issue";
import ConfigService from "hermes/services/config";
import FetchService from "hermes/services/fetch";
import {
HermesProject,
HermesProjectHit,
JiraIssue,
} from "hermes/types/project";
import StoreService from "hermes/services/store";
import { HermesProject, HermesProjectHit } from "hermes/types/project";

export const PROJECT_TILE_MAX_PRODUCTS = 3;

Expand All @@ -27,6 +26,7 @@ interface ProjectTileComponentSignature {
export default class ProjectTileComponent extends Component<ProjectTileComponentSignature> {
@service("fetch") declare fetchSvc: FetchService;
@service("config") declare configSvc: ConfigService;
@service declare store: StoreService;

constructor(owner: unknown, args: ProjectTileComponentSignature["Args"]) {
super(owner, args);
Expand All @@ -47,7 +47,7 @@ export default class ProjectTileComponent extends Component<ProjectTileComponent
* Used in the template to determine whether to show Jira-related data.
* Set by the `fetchJiraIssue` task if the project has a jiraIssueID.
*/
@tracked protected jiraIssue: JiraIssue | null = null;
@tracked protected jiraIssue: JiraIssueModel | null = null;

/**
* The project ID used as our LinkTo model.
Expand Down Expand Up @@ -96,13 +96,14 @@ export default class ProjectTileComponent extends Component<ProjectTileComponent
* Called in the constructor if the project has a jiraIssueID.
*/
protected fetchJiraIssue = task(async () => {
const jiraIssue = await this.fetchSvc
.fetch(
`/api/${this.configSvc.config.api_version}/jira/issues/${this.args.project.jiraIssueID}`,
)
.then((resp) => resp?.json());
assert("jiraIssueID must exist", this.args.project.jiraIssueID);

this.jiraIssue = jiraIssue as JiraIssue;
const jiraIssue = await this.store.findRecord(
"jira-issue",
this.args.project.jiraIssueID,
);

this.jiraIssue = jiraIssue;
});
}

Expand Down
51 changes: 51 additions & 0 deletions web/app/models/jira-issue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Model, { attr } from "@ember-data/model";

export default class JiraIssueModel extends Model {
/**
* The Jira issue key, e.g., "PROJ-123".
* Used as the ID for the JiraIssue model.
*/
@attr declare key: string;

/**
* The summary of the issue.
*/
@attr declare summary?: string;

/**
* The issue permalink.
*/
@attr declare url: string;

/**
* The status, e.g., "Open".
*/
@attr declare status: string;

/**
* The assignee of the issue.
*/
@attr declare assignee?: string;

/**
* The type of issue, e.g., "Bug".
*/
@attr declare issueType?: string;

/**
* The URL to the issue type image.
* TODO: Host these ourselves to avoid broken images.
*/
@attr declare issueTypeImage: string;

/**
* The issue priority, e.g., "High".
*/
@attr declare priority?: string;

/**
* The URL to the priority image.
* TODO: Host these ourselves to avoid broken images.
*/
@attr declare priorityImage: string;
}
25 changes: 25 additions & 0 deletions web/app/serializers/jira-issue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import JSONSerializer from "@ember-data/serializer/json";
import DS from "ember-data";
import JiraIssueModel from "hermes/models/jira-issue";

export default class JiraIssueSerializer extends JSONSerializer {
/**
* The serializer for the JiraIssue model.
* Formats responses to the JSON spec.
*/
normalizeResponse(
_store: DS.Store,
_primaryModelClass: any,
payload: JiraIssueModel,
_id: string | number,
_requestType: string,
) {
return {
data: {
id: payload.key,
type: "jira-issue",
attributes: payload,
},
};
}
}
4 changes: 0 additions & 4 deletions web/app/styles/animations.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
}
}

.fade-in-forwards {
animation: fadeIn 450ms forwards;
}

@keyframes fadeOut {
from {
opacity: 1;
Expand Down
15 changes: 2 additions & 13 deletions web/app/types/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,7 @@ import {
import { ProjectStatus } from "./project-status";
import { HermesDocument } from "./document";
import { AlgoliaHit } from "hermes/services/algolia";

export interface JiraIssue {
key: string;
url: string;
priority: string;
priorityImage: string;
status: string;
assignee?: string;
issueType: string;
issueTypeImage: string;
summary: string;
}
import JiraIssueModel from "hermes/models/jira-issue";

export interface JiraPickerResult {
key: string;
Expand Down Expand Up @@ -47,5 +36,5 @@ export interface HermesProjectResources {
export interface HermesProject
extends HermesProjectInfo,
HermesProjectResources {
jiraIssue?: JiraIssue;
jiraIssue?: JiraIssueModel;
}
5 changes: 3 additions & 2 deletions web/tests/integration/components/project/jira-widget-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { MirageTestContext, setupMirage } from "ember-cli-mirage/test-support";
import { setupRenderingTest } from "ember-qunit";
import { click, fillIn, find, render, waitFor } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { JiraIssue, JiraPickerResult } from "hermes/types/project";
import { JiraPickerResult } from "hermes/types/project";
import {
TEST_JIRA_WORKSPACE_URL,
TEST_JIRA_ISSUE_SUMMARY,
setWebConfig,
} from "hermes/mirage/utils";
import JiraIssueModel from "hermes/models/jira-issue";

const JIRA_ICON = "[data-test-jira-icon]";
const ADD_JIRA_INPUT = "[data-test-add-jira-input]";
Expand All @@ -33,7 +34,7 @@ const PLUS_ICON = "[data-test-add-jira-button-plus]";

interface Context extends MirageTestContext {
contextIsForm: boolean;
issue: JiraPickerResult | JiraIssue;
issue: JiraPickerResult | JiraIssueModel;
isLoading: boolean;
onIssueSelect: () => void;
onIssueRemove: () => void;
Expand Down

0 comments on commit 6f50b29

Please sign in to comment.