Skip to content

Instantly share code, notes, and snippets.

@peterbsmyth
Last active November 6, 2017 18:14
Show Gist options
  • Save peterbsmyth/8b569c54cb000f48727d8d4f59e25b19 to your computer and use it in GitHub Desktop.
Save peterbsmyth/8b569c54cb000f48727d8d4f59e25b19 to your computer and use it in GitHub Desktop.
Showing an architectural question
import { Injectable } from '@angular/core';
import { Effect, Actions } from '@ngrx/effects';
import { Action } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { Scheduler } from 'rxjs/Scheduler';
import { of } from 'rxjs/observable/of';
import { handleError } from './handleError';
import { GithubService } from '../services/github.service';
import * as githubActions from '../actions/github';
@Injectable()
export class GitHubEffects {
repoName: any;
action: any;
@Effect()
postRepo$: Observable<Action> = this.actions$
.ofType(githubActions.POST_REPO)
// store the action for later use
.do(action => this.action = action)
.map((action: githubActions.PostRepo) => action.payload.postRepo)
// add the repo
.switchMap((data) => this.githubService.postRepo(data))
.do((repo: any) => this.repoName = repo.name)
// get the master branch
.switchMap((repo: any) => this.githubService.getMasterBranch(repo.name))
// get the sha for README
.switchMap((repo: any) => this.githubService.getFiles(this.repoName, repo))
.map((files: any) => files.tree
.filter(file => file.path === 'README.md')
.map(file => file.sha)[0])
// update README with form input
.switchMap((sha) => {
this.action.payload.putFile.sha = sha;
return this.githubService.putFile(this.repoName, this.action.payload.putFile);
})
.map((res) => new githubActions.PostRepoComplete(res))
.catch(handleError);
constructor(
private actions$: Actions,
private githubService: GithubService,
) {}
}
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { environment } from '../../environments/environment';
@Injectable()
export class GithubService {
constructor(private http: HttpClient) { }
postRepo(data): Observable<object> {
return this.http.post(`https://api.github.com/user/repos`, data);
}
putFile(repoName, data): Observable<object> {
return this.http.put(`https://api.github.com/repos/peterbsmith2/${repoName}/contents/${data.path}`, data);
}
postFile(data): Observable<object> {
return this.http.put(`https://api.github.com/repos/peterbsmith2/read-test/contents/chapter_01.md`, data);
}
getRepos(): Observable<object> {
return this.http.get(`https://api.github.com/user/repos?per_page=100&affiliation=owner`);
}
getFiles(name, repo): Observable<object> {
const tree = repo.commit.commit.tree.sha;
return this.http.get(`https://api.github.com/repos/peterbsmith2/${name}/git/trees/${tree}`);
}
getMasterBranch(repo): Observable<object> {
return this.http.get(`https://api.github.com/repos/peterbsmith2/${repo}/branches/master`);
}
}
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Store } from '@ngrx/store';
import * as fromRoot from '../../reducers';
import * as app from '../../actions/app';
import * as githubActions from '../../actions/github';
import outdent from 'outdent';
/**
* Class that acts as smart container for the SettingsFormComponent
*/
@Component({
selector: 'cms-settings',
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SettingsComponent implements OnInit {
/**
* Constructor injection and assigning the original name to name$
* @param {Store<fromRoot.State>} store - Injection of store
*/
constructor(
private store: Store<fromRoot.State>,
) { }
/**
* Dispatches a SetNameAction with the name on the submitted form as the payload
* @param form - Form object that is to be submitted
*/
submit(form) {
let formatted;
if (form.chapters.length > 0) {
formatted = outdent`
# ${form.title}
## ${form.subtitle}
## ${form.author}
[Amazon](${form.amazonLink}) | [B&N](${form.bnLink})
## Chapters
${form.chapters.map(chapter => `**${chapter.chapterNumber}.** ${chapter.chapterTitle} `).join('\n')}
`;
} else {
formatted = outdent`
# ${form.title}
## ${form.subtitle}
## ${form.author}
[Amazon](${form.amazonLink}) | [B&N](${form.bnLink})
## Chapters
`;
}
const postRepo = {
name: `read-${form.title.toLowerCase().split(' ').join('-')}`,
description: `${form.title}`,
has_issues: false,
has_projects: false,
has_wiki: false,
auto_init: true,
allow_squash_merge: false,
allow_merge_commit: false,
};
const putFile = {
path: 'README.md',
message: 'add README',
content: btoa(formatted),
sha: '',
committer: {
name: 'Peter B Smith',
email: 'peter@upstate.agency'
}
};
this.store.dispatch(new githubActions.PostRepo({postRepo, putFile}));
}
/**
* Doesn't do anything
*/
ngOnInit() {
}
}
@peterbsmyth
Copy link
Author

peterbsmyth commented Nov 6, 2017

Intro

submit(form) in settings.component.ts dispatches an action with side effects that is handled in github.effects.ts, which fires a chain of github API requests that are contained in github.service.ts. The result is:

  1. a new repository is added to my github account
  2. that new repository has a README.md file that contains the contents of the form from submit(form).

Most Important Immediate Question

On line 23 of github.effects.ts I use a .do operator to save the action for re-use later on line 38. I'm convinced there is a better way to architect this.

@peterbsmyth
Copy link
Author

New Challenge

Desired outcome:

  1. call this.githubService.postRepo(repo)
  2. call this.githubService.getMasterBranch(repo.name)
  3. After getMasterBranch, use the repo from 1. as well as the branch from 2. as inputs to `getFiles(repo, branch)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment